Initial Contribution
diff --git a/opengl/libGLES_CM/Android.mk b/opengl/libGLES_CM/Android.mk
new file mode 100644
index 0000000..a0081ef
--- /dev/null
+++ b/opengl/libGLES_CM/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Build the wrapper OpenGL ES library
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= gl_wrapper.cpp.arm gl_logger.cpp
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libui
+LOCAL_LDLIBS := -lpthread -ldl
+LOCAL_MODULE:= libGLES_CM
+
+# needed on sim build because of weird logging issues
+ifeq ($(TARGET_SIMULATOR),true)
+else
+	LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/opengl/libGLES_CM/egl_entries.cpp b/opengl/libGLES_CM/egl_entries.cpp
new file mode 100644
index 0000000..ba46f40
--- /dev/null
+++ b/opengl/libGLES_CM/egl_entries.cpp
@@ -0,0 +1,46 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)
+
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint *)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface,  EGLDisplay, EGLConfig, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface,  EGLDisplay, EGLSurface, EGLint, EGLint *)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglQueryContext,  EGLDisplay, EGLContext, EGLint, EGLint *)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(proc_t, eglGetProcAddress, const char *)
+
+/* EGL 1.1 */
+
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+
+/* EGL 1.2 */
+
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *)
+
+/* Android extentions */
+
+EGL_ENTRY(EGLBoolean, eglSwapRectangleANDROID, EGLDisplay, EGLSurface , EGLint, EGLint, EGLint, EGLint)
+EGL_ENTRY(const char*, eglQueryStringConfigANDROID, EGLDisplay, EGLConfig, EGLint)
diff --git a/opengl/libGLES_CM/enumextract.sh b/opengl/libGLES_CM/enumextract.sh
new file mode 100644
index 0000000..5707302
--- /dev/null
+++ b/opengl/libGLES_CM/enumextract.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+awk '
+/^#define GL_/ {
+    names[count] = $2;
+    values[count] = $3;
+    sort[count] = $3 + 0;
+    count++;
+}
+END {
+    for (i = 1; i < count; i++) {
+        for (j = 0; j < i; j++) {
+            if (sort[i] < sort[j]) {
+                tn = names[i];
+                tv = values[i];
+                ts = sort[i];
+                names[i] = names[j];
+                values[i] = values[j];
+                sort[i] = sort[j];
+                names[j] = tn;
+                values[j] = tv;
+                sort[j] = ts;
+            }
+        }
+    }
+ 
+    for (i = 0; i < count; i++) {
+        printf("GLENUM(%s, %s)\n", names[i], values[i]);
+    }
+}
+' < $1
+
diff --git a/opengl/libGLES_CM/gl_api.cpp b/opengl/libGLES_CM/gl_api.cpp
new file mode 100644
index 0000000..ed3bdaa
--- /dev/null
+++ b/opengl/libGLES_CM/gl_api.cpp
@@ -0,0 +1,606 @@
+void API_ENTRY(glActiveTexture)(GLenum texture) {
+    CALL_GL_API(glActiveTexture, texture);
+}
+
+void API_ENTRY(glAlphaFunc)(GLenum func, GLclampf ref) {
+    CALL_GL_API(glAlphaFunc, func, ref);
+}
+
+void API_ENTRY(glAlphaFuncx)(GLenum func, GLclampx ref) {
+    CALL_GL_API(glAlphaFuncx, func, ref);
+}
+
+void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) {
+    CALL_GL_API(glBindTexture, target, texture);
+}
+
+void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) {
+    CALL_GL_API(glBlendFunc, sfactor, dfactor);
+}
+
+void API_ENTRY(glClear)(GLbitfield mask) {
+    CALL_GL_API(glClear, mask);
+}
+
+void API_ENTRY(glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
+    CALL_GL_API(glClearColor, red, green, blue, alpha);
+}
+
+void API_ENTRY(glClearColorx)(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) {
+    CALL_GL_API(glClearColorx, red, green, blue, alpha);
+}
+
+void API_ENTRY(glClearDepthf)(GLclampf depth) {
+    CALL_GL_API(glClearDepthf, depth);
+}
+
+void API_ENTRY(glClearDepthx)(GLclampx depth) {
+    CALL_GL_API(glClearDepthx, depth);
+}
+
+void API_ENTRY(glClearStencil)(GLint s) {
+    CALL_GL_API(glClearStencil, s);
+}
+
+void API_ENTRY(glClientActiveTexture)(GLenum texture) {
+    CALL_GL_API(glClientActiveTexture, texture);
+}
+
+void API_ENTRY(glColor4f)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+    CALL_GL_API(glColor4f, red, green, blue, alpha);
+}
+
+void API_ENTRY(glColor4x)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) {
+    CALL_GL_API(glColor4x, red, green, blue, alpha);
+}
+
+void API_ENTRY(glColorMask)(GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+    CALL_GL_API(glColorMask, r, g, b, a);
+}
+
+void API_ENTRY(glColorPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr)
+{
+    CALL_GL_API(glColorPointer, size, type, stride, ptr);
+}
+
+void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat,
+                            GLsizei width, GLsizei height, GLint border,
+                            GLsizei imageSize, const GLvoid *data) {
+    CALL_GL_API(glCompressedTexImage2D, target, level, internalformat,
+            width, height, border, imageSize, data);
+}
+
+void API_ENTRY(glCompressedTexSubImage2D)( GLenum target, GLint level, GLint xoffset,
+                                GLint yoffset, GLsizei width, GLsizei height,
+                                GLenum format, GLsizei imageSize,
+                                const GLvoid *data) {
+    CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset,
+            width, height, format, imageSize, data);
+}
+
+void API_ENTRY(glCopyTexImage2D)(  GLenum target, GLint level, GLenum internalformat,
+                        GLint x, GLint y, GLsizei width, GLsizei height,
+                        GLint border) {
+    CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y,
+            width, height, border);
+}
+
+void API_ENTRY(glCopyTexSubImage2D)(   GLenum target, GLint level, GLint xoffset,
+                            GLint yoffset, GLint x, GLint y, GLsizei width,
+                            GLsizei height) {
+    CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y,
+            width, height);
+}
+
+void API_ENTRY(glCullFace)(GLenum mode) {
+    CALL_GL_API(glCullFace, mode);
+}
+
+void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) {
+    CALL_GL_API(glDeleteTextures, n, textures);
+}
+
+void API_ENTRY(glDepthFunc)(GLenum func) {
+    CALL_GL_API(glDepthFunc, func);
+}
+
+void API_ENTRY(glDepthMask)(GLboolean flag) {
+    CALL_GL_API(glDepthMask, flag);
+}
+
+void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) {
+    CALL_GL_API(glDepthRangef, zNear, zFar);
+}
+
+void API_ENTRY(glDepthRangex)(GLclampx zNear, GLclampx zFar) {
+    CALL_GL_API(glDepthRangex, zNear, zFar);
+}
+
+void API_ENTRY(glDisable)(GLenum cap) {
+    CALL_GL_API(glDisable, cap);
+}
+
+void API_ENTRY(glDisableClientState)(GLenum array) {
+    CALL_GL_API(glDisableClientState, array);
+}
+
+void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) {
+    CALL_GL_API(glDrawArrays, mode, first, count);
+}
+
+void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count,
+                    GLenum type, const GLvoid *indices) {
+    CALL_GL_API(glDrawElements, mode, count, type, indices);
+}
+
+void API_ENTRY(glEnable)(GLenum cap) {
+    CALL_GL_API(glEnable, cap);
+}
+
+void API_ENTRY(glEnableClientState)(GLenum array) {
+    CALL_GL_API(glEnableClientState, array);
+}
+
+void API_ENTRY(glFinish)(void) {
+    CALL_GL_API(glFinish);
+}
+
+void API_ENTRY(glFlush)(void) {
+    CALL_GL_API(glFlush);
+}
+
+void API_ENTRY(glFogf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glFogf, pname, param);
+}
+
+void API_ENTRY(glFogfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glFogfv, pname, params);
+}
+
+void API_ENTRY(glFogx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glFogx, pname, param);
+}
+
+void API_ENTRY(glFogxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glFogxv, pname, params);
+}
+
+void API_ENTRY(glFrontFace)(GLenum mode) {
+    CALL_GL_API(glFrontFace, mode);
+}
+
+void API_ENTRY(glFrustumf)(GLfloat left, GLfloat right,
+                GLfloat bottom, GLfloat top,
+                GLfloat zNear, GLfloat zFar) {
+    CALL_GL_API(glFrustumf, left, right, bottom, top, zNear, zFar);
+}
+
+void API_ENTRY(glFrustumx)(GLfixed left, GLfixed right,
+                GLfixed bottom, GLfixed top,
+                GLfixed zNear, GLfixed zFar) {
+    CALL_GL_API(glFrustumx, left, right, bottom, top, zNear, zFar);
+}
+
+void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) {
+    CALL_GL_API(glGenTextures, n, textures);
+}
+
+GLenum API_ENTRY(glGetError)(void) {
+    CALL_GL_API_RETURN(glGetError);
+}
+
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *params) {
+    CALL_GL_API(glGetIntegerv, pname, params);
+}
+
+const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+    CALL_GL_API_RETURN(glGetString, name);
+}
+
+void API_ENTRY(glHint)(GLenum target, GLenum mode) {
+    CALL_GL_API(glHint, target, mode);
+}
+
+void API_ENTRY(glLightModelf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glLightModelf, pname, param);
+}
+
+void API_ENTRY(glLightModelfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glLightModelfv, pname, params);
+}
+
+void API_ENTRY(glLightModelx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glLightModelx, pname, param);
+}
+
+void API_ENTRY(glLightModelxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glLightModelxv, pname, params);
+}
+
+void API_ENTRY(glLightf)(GLenum light, GLenum pname, GLfloat param) {
+    CALL_GL_API(glLightf, light, pname, param);
+}
+
+void API_ENTRY(glLightfv)(GLenum light, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glLightfv, light, pname, params);
+}
+
+void API_ENTRY(glLightx)(GLenum light, GLenum pname, GLfixed param) {
+    CALL_GL_API(glLightx, light, pname, param);
+}
+
+void API_ENTRY(glLightxv)(GLenum light, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glLightxv, light, pname, params);
+}
+
+void API_ENTRY(glLineWidth)(GLfloat width) {
+    CALL_GL_API(glLineWidth, width);
+}
+
+void API_ENTRY(glLineWidthx)(GLfixed width) {
+    CALL_GL_API(glLineWidthx, width);
+}
+
+void API_ENTRY(glLoadIdentity)(void) {
+    CALL_GL_API(glLoadIdentity);
+}
+
+void API_ENTRY(glLoadMatrixf)(const GLfloat *m) {
+    CALL_GL_API(glLoadMatrixf, m);
+}
+
+void API_ENTRY(glLoadMatrixx)(const GLfixed *m) {
+    CALL_GL_API(glLoadMatrixx, m);
+}
+
+void API_ENTRY(glLogicOp)(GLenum opcode) {
+    CALL_GL_API(glLogicOp, opcode);
+}
+
+void API_ENTRY(glMaterialf)(GLenum face, GLenum pname, GLfloat param) {
+    CALL_GL_API(glMaterialf, face, pname, param);
+}
+
+void API_ENTRY(glMaterialfv)(GLenum face, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glMaterialfv, face, pname, params);
+}
+
+void API_ENTRY(glMaterialx)(GLenum face, GLenum pname, GLfixed param) {
+    CALL_GL_API(glMaterialx, face, pname, param);
+}
+
+void API_ENTRY(glMaterialxv)(GLenum face, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glMaterialxv, face, pname, params);
+}
+
+void API_ENTRY(glMatrixMode)(GLenum mode) {
+    CALL_GL_API(glMatrixMode, mode);
+}
+
+void API_ENTRY(glMultMatrixf)(const GLfloat *m) {
+    CALL_GL_API(glMultMatrixf, m);
+}
+
+void API_ENTRY(glMultMatrixx)(const GLfixed *m) {
+    CALL_GL_API(glMultMatrixx, m);
+}
+
+void API_ENTRY(glMultiTexCoord4f)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) {
+    CALL_GL_API(glMultiTexCoord4f, target, s, t, r, q);
+}
+
+void API_ENTRY(glMultiTexCoord4x)(GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) {
+    CALL_GL_API(glMultiTexCoord4x, target, s, t, r, q);
+}
+
+void API_ENTRY(glNormal3f)(GLfloat nx, GLfloat ny, GLfloat nz) {
+    CALL_GL_API(glNormal3f, nx, ny, nz);
+}
+
+void API_ENTRY(glNormal3x)(GLfixed nx, GLfixed ny, GLfixed nz) {
+    CALL_GL_API(glNormal3x, nx, ny, nz);
+}
+
+void API_ENTRY(glNormalPointer)(GLenum type, GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glNormalPointer, type, stride, pointer);
+}
+
+void API_ENTRY(glOrthof)(  GLfloat left, GLfloat right,
+                GLfloat bottom, GLfloat top,
+                GLfloat zNear, GLfloat zFar) {
+    CALL_GL_API(glOrthof, left, right, bottom, top, zNear, zFar);
+}
+
+void API_ENTRY(glOrthox)(  GLfixed left, GLfixed right,
+                GLfixed bottom, GLfixed top,
+                GLfixed zNear, GLfixed zFar) {
+    CALL_GL_API(glOrthox, left, right, bottom, top, zNear, zFar);
+}
+
+void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) {
+    CALL_GL_API(glPixelStorei, pname, param);
+}
+
+void API_ENTRY(glPointSize)(GLfloat size) {
+    CALL_GL_API(glPointSize, size);
+}
+
+void API_ENTRY(glPointSizex)(GLfixed size) {
+    CALL_GL_API(glPointSizex, size);
+}
+
+void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) {
+    CALL_GL_API(glPolygonOffset, factor, units);
+}
+
+void API_ENTRY(glPolygonOffsetx)(GLfixed factor, GLfixed units) {
+    CALL_GL_API(glPolygonOffsetx, factor, units);
+}
+
+void API_ENTRY(glPopMatrix)(void) {
+    CALL_GL_API(glPopMatrix);
+}
+
+void API_ENTRY(glPushMatrix)(void) {
+    CALL_GL_API(glPushMatrix);
+}
+
+void API_ENTRY(glReadPixels)(  GLint x, GLint y, GLsizei width, GLsizei height,
+                    GLenum format, GLenum type, GLvoid *pixels) {
+    CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels);
+}
+
+void API_ENTRY(glRotatef)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glRotatef, angle, x, y, z);
+}
+
+void API_ENTRY(glRotatex)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glRotatex, angle, x, y, z);
+}
+
+void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) {
+    CALL_GL_API(glSampleCoverage, value, invert);
+}
+
+void API_ENTRY(glSampleCoveragex)(GLclampx value, GLboolean invert) {
+    CALL_GL_API(glSampleCoveragex, value, invert);
+}
+
+void API_ENTRY(glScalef)(GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glScalef, x, y, z);
+}
+
+void API_ENTRY(glScalex)(GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glScalex, x, y, z);
+}
+
+void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) {
+    CALL_GL_API(glScissor, x, y, width, height);
+}
+
+void API_ENTRY(glShadeModel)(GLenum mode) {
+    CALL_GL_API(glShadeModel, mode);
+}
+
+void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
+    CALL_GL_API(glStencilFunc, func, ref, mask);
+}
+
+void API_ENTRY(glStencilMask)(GLuint mask) {
+    CALL_GL_API(glStencilMask, mask);
+}
+
+void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) {
+    CALL_GL_API(glStencilOp, fail, zfail, zpass);
+}
+
+void API_ENTRY(glTexCoordPointer)( GLint size, GLenum type,
+                        GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glTexCoordPointer, size, type, stride, pointer);
+}
+
+void API_ENTRY(glTexEnvf)(GLenum target, GLenum pname, GLfloat param) {
+    CALL_GL_API(glTexEnvf, target, pname, param);
+}
+
+void API_ENTRY(glTexEnvfv)(GLenum target, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glTexEnvfv, target, pname, params);
+}
+
+void API_ENTRY(glTexEnvx)(GLenum target, GLenum pname, GLfixed param) {
+    CALL_GL_API(glTexEnvx, target, pname, param);
+}
+
+void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glTexEnvxv, target, pname, params);
+}
+
+void API_ENTRY(glTexImage2D)(  GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLint border, GLenum format,
+                    GLenum type, const GLvoid *pixels) {
+    CALL_GL_API(glTexImage2D, target, level, internalformat, width, height,
+            border, format, type, pixels);
+}
+
+void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
+    CALL_GL_API(glTexParameterf, target, pname, param);
+}
+
+void API_ENTRY(glTexParameterx)(GLenum target, GLenum pname, GLfixed param) {
+    CALL_GL_API(glTexParameterx, target, pname, param);
+}
+
+void API_ENTRY(glTexSubImage2D)(   GLenum target, GLint level, GLint xoffset,
+                        GLint yoffset, GLsizei width, GLsizei height,
+                        GLenum format, GLenum type, const GLvoid *pixels) {
+    CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset,
+            width, height, format, type, pixels);
+}
+
+void API_ENTRY(glTranslatef)(GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glTranslatef, x, y, z);
+}
+
+void API_ENTRY(glTranslatex)(GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glTranslatex, x, y, z);
+}
+
+void API_ENTRY(glVertexPointer)(   GLint size, GLenum type,
+                        GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glVertexPointer, size, type, stride, pointer);
+}
+
+void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) {
+    CALL_GL_API(glViewport, x, y, width, height);
+}
+
+// ES 1.1
+void API_ENTRY(glClipPlanef)(GLenum plane, const GLfloat *equation) {
+    CALL_GL_API(glClipPlanef, plane, equation);
+}
+void API_ENTRY(glClipPlanex)(GLenum plane, const GLfixed *equation) {
+    CALL_GL_API(glClipPlanex, plane, equation);
+}
+void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) {
+    CALL_GL_API(glBindBuffer, target, buffer);
+}
+void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
+    CALL_GL_API(glBufferData, target, size, data, usage);
+}
+void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) {
+    CALL_GL_API(glBufferSubData, target, offset, size, data);
+}
+void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint* buffers) {
+    CALL_GL_API(glDeleteBuffers, n, buffers);
+}
+void API_ENTRY(glGenBuffers)(GLsizei n, GLuint* buffers) {
+    CALL_GL_API(glGenBuffers, n, buffers);
+}
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *params) {
+    CALL_GL_API(glGetBooleanv, pname, params);
+}
+void API_ENTRY(glGetFixedv)(GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetFixedv, pname, params);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetFloatv, pname, params);
+}
+void API_ENTRY(glGetPointerv)(GLenum pname, void **params) {
+    CALL_GL_API(glGetPointerv, pname, params);
+}
+void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
+    CALL_GL_API(glGetBufferParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetClipPlanef)(GLenum pname, GLfloat eqn[4]) {
+    CALL_GL_API(glGetClipPlanef, pname, eqn);
+}
+void API_ENTRY(glGetClipPlanex)(GLenum pname, GLfixed eqn[4]) {
+    CALL_GL_API(glGetClipPlanex, pname, eqn);
+}
+void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetLightxv, light, pname, params);
+}
+void API_ENTRY(glGetLightfv)(GLenum light, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetLightfv, light, pname, params);
+}
+void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetMaterialxv, face, pname, params);
+}
+void API_ENTRY(glGetMaterialfv)(GLenum face, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetMaterialfv, face, pname, params);
+}
+void API_ENTRY(glGetTexEnvfv)(GLenum env, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetTexEnvfv, env, pname, params);
+}
+void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) {
+    CALL_GL_API(glGetTexEnviv, env, pname, params);
+}
+void API_ENTRY(glGetTexEnvxv)(GLenum env, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetTexEnvxv, env, pname, params);
+}
+void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetTexParameterfv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) {
+    CALL_GL_API(glGetTexParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterxv)(GLenum target, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetTexParameterxv, target, pname, params);
+}
+GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) {
+    CALL_GL_API_RETURN(glIsBuffer, buffer);
+}
+GLboolean API_ENTRY(glIsEnabled)(GLenum cap) {
+    CALL_GL_API_RETURN(glIsEnabled, cap);
+}
+GLboolean API_ENTRY(glIsTexture)(GLuint texture) {
+    CALL_GL_API_RETURN(glIsTexture, texture);
+}
+void API_ENTRY(glPointParameterf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glPointParameterf, pname, param);
+}
+void API_ENTRY(glPointParameterfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glPointParameterfv, pname, params);
+}
+void API_ENTRY(glPointParameterx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glPointParameterx, pname, param);
+}
+void API_ENTRY(glPointParameterxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glPointParameterxv, pname, params);
+}
+void API_ENTRY(glColor4ub)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) {
+    CALL_GL_API(glColor4ub, red, green, blue, alpha);
+}
+void API_ENTRY(glTexEnvi)(GLenum target, GLenum pname, GLint param) {
+    CALL_GL_API(glTexEnvi, target, pname, param);
+}
+void API_ENTRY(glTexEnviv)(GLenum target, GLenum pname, const GLint *params) {
+    CALL_GL_API(glTexEnviv, target, pname, params);
+}
+
+void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glTexParameterfv, target, pname, params);
+}
+
+void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) {
+    CALL_GL_API(glTexParameteriv, target, pname, params);
+}
+
+void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) {
+    CALL_GL_API(glTexParameteri, target, pname, param);
+}
+void API_ENTRY(glTexParameterxv)(GLenum target, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glTexParameterxv, target, pname, params);
+}
+void API_ENTRY(glPointSizePointerOES)(GLenum type, GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glPointSizePointerOES, type, stride, pointer);
+}
+
+// Extensions
+void API_ENTRY(glDrawTexsOES)(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
+    CALL_GL_API(glDrawTexsOES, x, y, z, w, h);
+}
+void API_ENTRY(glDrawTexiOES)(GLint x, GLint y, GLint z, GLint w, GLint h) {
+    CALL_GL_API(glDrawTexiOES, x, y, z, w, h);
+}
+void API_ENTRY(glDrawTexfOES)(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h) {
+    CALL_GL_API(glDrawTexfOES, x, y, z, w, h);
+}
+void API_ENTRY(glDrawTexxOES)(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
+    CALL_GL_API(glDrawTexxOES, x, y, z, w, h);
+}
+void API_ENTRY(glDrawTexsvOES)(const GLshort* coords) {
+    CALL_GL_API(glDrawTexsvOES, coords);
+}
+void API_ENTRY(glDrawTexivOES)(const GLint* coords) {
+    CALL_GL_API(glDrawTexivOES, coords);
+}
+void API_ENTRY(glDrawTexfvOES)(const GLfloat* coords) {
+    CALL_GL_API(glDrawTexfvOES, coords);
+}
+void API_ENTRY(glDrawTexxvOES)(const GLfixed* coords) {
+    CALL_GL_API(glDrawTexxvOES, coords);
+}
+GLbitfield API_ENTRY(glQueryMatrixxOES)(GLfixed* mantissa, GLint* exponent) {
+    CALL_GL_API_RETURN(glQueryMatrixxOES, mantissa, exponent);
+}
diff --git a/opengl/libGLES_CM/gl_entries.cpp b/opengl/libGLES_CM/gl_entries.cpp
new file mode 100644
index 0000000..3279322
--- /dev/null
+++ b/opengl/libGLES_CM/gl_entries.cpp
@@ -0,0 +1,159 @@
+GL_ENTRY(void, glColor4f, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glColor4x, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glNormal3f, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glNormal3x, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glCullFace, GLenum)
+GL_ENTRY(void, glFrontFace, GLenum)
+GL_ENTRY(void, glDisable, GLenum)
+GL_ENTRY(void, glEnable, GLenum)
+GL_ENTRY(void, glFinish, void)
+GL_ENTRY(void, glFlush, void)
+GL_ENTRY(GLenum, glGetError, void)
+GL_ENTRY(const GLubyte*, glGetString, GLenum)
+GL_ENTRY(void, glGetIntegerv, GLenum, GLint *)
+GL_ENTRY(void, glColorMask, GLboolean, GLboolean, GLboolean, GLboolean)
+GL_ENTRY(void, glDepthMask, GLboolean)
+GL_ENTRY(void, glStencilMask, GLuint)
+GL_ENTRY(void, glDepthFunc, GLenum)
+GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar)
+GL_ENTRY(void, glDepthRangex, GLclampx zNear, GLclampx zFar)
+GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
+GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units)
+GL_ENTRY(void, glLogicOp, GLenum opcode)
+GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref)
+GL_ENTRY(void, glAlphaFunc, GLenum func, GLclampf ref)
+GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor)
+GL_ENTRY(void, glClear, GLbitfield mask)
+GL_ENTRY(void, glClearColor, GLclampf r, GLclampf g, GLclampf b, GLclampf a)
+GL_ENTRY(void, glClearColorx, GLclampx r, GLclampx g, GLclampx b, GLclampx a)
+GL_ENTRY(void, glClearDepthf, GLclampf depth)
+GL_ENTRY(void, glClearDepthx, GLclampx depth)
+GL_ENTRY(void, glClearStencil, GLint s)
+GL_ENTRY(void, glPointSize, GLfloat)
+GL_ENTRY(void, glPointSizex, GLfixed)
+GL_ENTRY(void, glSampleCoverage, GLclampf value, GLboolean invert)
+GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert)
+GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glHint, GLenum, GLenum mode)
+GL_ENTRY(void, glLineWidth, GLfloat width)
+GL_ENTRY(void, glLineWidthx, GLfixed width)
+GL_ENTRY(void, glShadeModel, GLenum)
+GL_ENTRY(void, glLightModelf, GLenum, GLfloat)
+GL_ENTRY(void, glLightModelfv, GLenum, const GLfloat *)
+GL_ENTRY(void, glLightModelx, GLenum, GLfixed)
+GL_ENTRY(void, glLightModelxv, GLenum, const GLfixed *)
+GL_ENTRY(void, glLightf, GLenum, GLenum, GLfloat)
+GL_ENTRY(void, glLightfv, GLenum, GLenum, const GLfloat *)
+GL_ENTRY(void, glLightx, GLenum, GLenum, GLfixed)
+GL_ENTRY(void, glLightxv, GLenum, GLenum, const GLfixed *)
+GL_ENTRY(void, glMaterialf, GLenum, GLenum, GLfloat)
+GL_ENTRY(void, glMaterialfv, GLenum, GLenum, const GLfloat *)
+GL_ENTRY(void, glMaterialx, GLenum, GLenum, GLfixed)
+GL_ENTRY(void, glMaterialxv, GLenum, GLenum, const GLfixed *)
+GL_ENTRY(void, glFogf, GLenum, GLfloat)
+GL_ENTRY(void, glFogfv, GLenum, const GLfloat *)
+GL_ENTRY(void, glFogx, GLenum, GLfixed)
+GL_ENTRY(void, glFogxv, GLenum, const GLfixed *)
+GL_ENTRY(void, glVertexPointer, GLint, GLenum, GLsizei, const GLvoid *)
+GL_ENTRY(void, glColorPointer, GLint, GLenum, GLsizei, const GLvoid *)
+GL_ENTRY(void, glNormalPointer, GLenum, GLsizei, const GLvoid *)
+GL_ENTRY(void, glTexCoordPointer, GLint, GLenum, GLsizei, const GLvoid *)
+GL_ENTRY(void, glEnableClientState, GLenum)
+GL_ENTRY(void, glDisableClientState, GLenum)
+GL_ENTRY(void, glClientActiveTexture, GLenum)
+GL_ENTRY(void, glDrawArrays, GLenum, GLint first, GLsizei)
+GL_ENTRY(void, glDrawElements, GLenum, GLsizei, GLenum, const GLvoid *)
+GL_ENTRY(void, glLoadIdentity, void)
+GL_ENTRY(void, glLoadMatrixf, const GLfloat*)
+GL_ENTRY(void, glLoadMatrixx, const GLfixed*)
+GL_ENTRY(void, glMatrixMode, GLenum mode)
+GL_ENTRY(void, glMultMatrixf, const GLfloat*)
+GL_ENTRY(void, glMultMatrixx, const GLfixed*)
+GL_ENTRY(void, glPopMatrix, void)
+GL_ENTRY(void, glPushMatrix, void)
+GL_ENTRY(void, glFrustumf, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glFrustumx, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glOrthof, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glOrthox, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glRotatef, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glRotatex, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glScalef, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glScalex, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glTranslatef, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glTranslatex, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glViewport, GLint, GLint, GLsizei, GLsizei)
+GL_ENTRY(void, glActiveTexture, GLenum)
+GL_ENTRY(void, glBindTexture, GLenum, GLuint)
+GL_ENTRY(void, glGenTextures, GLsizei, GLuint*)
+GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *)
+GL_ENTRY(void, glMultiTexCoord4f, GLenum, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glMultiTexCoord4x, GLenum, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glPixelStorei, GLenum, GLint)
+GL_ENTRY(void, glTexEnvf, GLenum, GLenum, GLfloat)
+GL_ENTRY(void, glTexEnvfv, GLenum, GLenum, const GLfloat*)
+GL_ENTRY(void, glTexEnvx, GLenum, GLenum, GLfixed)
+GL_ENTRY(void, glTexEnvxv, GLenum, GLenum, const GLfixed*)
+GL_ENTRY(void, glTexParameterf, GLenum, GLenum, GLfloat)
+GL_ENTRY(void, glTexParameterx, GLenum, GLenum, GLfixed)
+GL_ENTRY(void, glCompressedTexImage2D,    GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*)
+GL_ENTRY(void, glCompressedTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*)
+GL_ENTRY(void, glCopyTexImage2D, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint)
+GL_ENTRY(void, glCopyTexSubImage2D, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei)
+GL_ENTRY(void, glTexImage2D, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*)
+GL_ENTRY(void, glTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*)
+GL_ENTRY(void, glReadPixels, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *)
+
+// 1.1 additions
+GL_ENTRY(void, glClipPlanef, GLenum plane, const GLfloat*)
+GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed*)
+GL_ENTRY(void, glBindBuffer, GLenum, GLuint)
+GL_ENTRY(void, glBufferData, GLenum, GLsizeiptr, const GLvoid*, GLenum)
+GL_ENTRY(void, glBufferSubData, GLenum, GLintptr, GLsizeiptr, const GLvoid*)
+GL_ENTRY(void, glDeleteBuffers, GLsizei, const GLuint*)
+GL_ENTRY(void, glGenBuffers, GLsizei, GLuint*)
+GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean *)
+GL_ENTRY(void, glGetFixedv, GLenum, GLfixed *)
+GL_ENTRY(void, glGetFloatv, GLenum, GLfloat *)
+GL_ENTRY(void, glGetPointerv, GLenum, void **)
+GL_ENTRY(void, glGetBufferParameteriv, GLenum, GLenum, GLint *)
+GL_ENTRY(void, glGetClipPlanef, GLenum, GLfloat[4])
+GL_ENTRY(void, glGetClipPlanex, GLenum, GLfixed[4])
+GL_ENTRY(void, glGetLightxv, GLenum, GLenum, GLfixed *)
+GL_ENTRY(void, glGetLightfv, GLenum, GLenum, GLfloat *)
+GL_ENTRY(void, glGetMaterialxv, GLenum, GLenum, GLfixed *)
+GL_ENTRY(void, glGetMaterialfv, GLenum, GLenum, GLfloat *)
+GL_ENTRY(void, glGetTexEnvfv, GLenum, GLenum, GLfloat *)
+GL_ENTRY(void, glGetTexEnviv, GLenum, GLenum, GLint *)
+GL_ENTRY(void, glGetTexEnvxv, GLenum, GLenum, GLfixed *)
+GL_ENTRY(void, glGetTexParameterfv, GLenum, GLenum, GLfloat *)
+GL_ENTRY(void, glGetTexParameteriv, GLenum, GLenum, GLint *)
+GL_ENTRY(void, glGetTexParameterxv, GLenum, GLenum, GLfixed *)
+GL_ENTRY(GLboolean, glIsBuffer, GLuint)
+GL_ENTRY(GLboolean, glIsEnabled, GLenum)
+GL_ENTRY(GLboolean, glIsTexture, GLuint)
+GL_ENTRY(void, glPointParameterf, GLenum, GLfloat)
+GL_ENTRY(void, glPointParameterfv, GLenum, const GLfloat *)
+GL_ENTRY(void, glPointParameterx, GLenum, GLfixed)
+GL_ENTRY(void, glPointParameterxv, GLenum, const GLfixed *)
+GL_ENTRY(void, glColor4ub, GLubyte, GLubyte, GLubyte, GLubyte)
+GL_ENTRY(void, glTexEnvi, GLenum, GLenum, GLint)
+GL_ENTRY(void, glTexEnviv, GLenum, GLenum, const GLint *)
+GL_ENTRY(void, glTexParameterfv, GLenum, GLenum, const GLfloat *)
+GL_ENTRY(void, glTexParameteriv, GLenum, GLenum, const GLint *)
+GL_ENTRY(void, glTexParameteri, GLenum, GLenum, GLint)
+GL_ENTRY(void, glTexParameterxv, GLenum, GLenum, const GLfixed *)
+GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const GLvoid*)
+
+// Extensions
+GL_ENTRY(void, glDrawTexsOES, GLshort, GLshort, GLshort, GLshort, GLshort)
+GL_ENTRY(void, glDrawTexiOES, GLint, GLint, GLint, GLint, GLint)
+GL_ENTRY(void, glDrawTexfOES, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat)
+GL_ENTRY(void, glDrawTexxOES, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed)
+GL_ENTRY(void, glDrawTexsvOES, const GLshort*)
+GL_ENTRY(void, glDrawTexivOES, const GLint*)
+GL_ENTRY(void, glDrawTexfvOES, const GLfloat*)
+GL_ENTRY(void, glDrawTexxvOES, const GLfixed*)
+GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed* mantissa, GLint* exponent)
+
diff --git a/opengl/libGLES_CM/gl_enums.in b/opengl/libGLES_CM/gl_enums.in
new file mode 100644
index 0000000..ffc2fad
--- /dev/null
+++ b/opengl/libGLES_CM/gl_enums.in
@@ -0,0 +1,261 @@
+GLENUM(GL_POINTS, 0x0000)
+GLENUM(GL_LINES, 0x0001)
+GLENUM(GL_LINE_LOOP, 0x0002)
+GLENUM(GL_LINE_STRIP, 0x0003)
+GLENUM(GL_TRIANGLES, 0x0004)
+GLENUM(GL_TRIANGLE_STRIP, 0x0005)
+GLENUM(GL_TRIANGLE_FAN, 0x0006)
+GLENUM(GL_ADD, 0x0104)
+GLENUM(GL_NEVER, 0x0200)
+GLENUM(GL_LESS, 0x0201)
+GLENUM(GL_EQUAL, 0x0202)
+GLENUM(GL_LEQUAL, 0x0203)
+GLENUM(GL_GREATER, 0x0204)
+GLENUM(GL_NOTEQUAL, 0x0205)
+GLENUM(GL_GEQUAL, 0x0206)
+GLENUM(GL_ALWAYS, 0x0207)
+GLENUM(GL_SRC_COLOR, 0x0300)
+GLENUM(GL_ONE_MINUS_SRC_COLOR, 0x0301)
+GLENUM(GL_SRC_ALPHA, 0x0302)
+GLENUM(GL_ONE_MINUS_SRC_ALPHA, 0x0303)
+GLENUM(GL_DST_ALPHA, 0x0304)
+GLENUM(GL_ONE_MINUS_DST_ALPHA, 0x0305)
+GLENUM(GL_DST_COLOR, 0x0306)
+GLENUM(GL_ONE_MINUS_DST_COLOR, 0x0307)
+GLENUM(GL_SRC_ALPHA_SATURATE, 0x0308)
+GLENUM(GL_FRONT, 0x0404)
+GLENUM(GL_BACK, 0x0405)
+GLENUM(GL_FRONT_AND_BACK, 0x0408)
+GLENUM(GL_INVALID_ENUM, 0x0500)
+GLENUM(GL_INVALID_VALUE, 0x0501)
+GLENUM(GL_INVALID_OPERATION, 0x0502)
+GLENUM(GL_STACK_OVERFLOW, 0x0503)
+GLENUM(GL_STACK_UNDERFLOW, 0x0504)
+GLENUM(GL_OUT_OF_MEMORY, 0x0505)
+GLENUM(GL_EXP, 0x0800)
+GLENUM(GL_EXP2, 0x0801)
+GLENUM(GL_CW, 0x0900)
+GLENUM(GL_CCW, 0x0901)
+GLENUM(GL_POINT_SMOOTH, 0x0B10)
+GLENUM(GL_SMOOTH_POINT_SIZE_RANGE, 0x0B12)
+GLENUM(GL_LINE_SMOOTH, 0x0B20)
+GLENUM(GL_SMOOTH_LINE_WIDTH_RANGE, 0x0B22)
+GLENUM(GL_CULL_FACE, 0x0B44)
+GLENUM(GL_LIGHTING, 0x0B50)
+GLENUM(GL_LIGHT_MODEL_TWO_SIDE, 0x0B52)
+GLENUM(GL_LIGHT_MODEL_AMBIENT, 0x0B53)
+GLENUM(GL_COLOR_MATERIAL, 0x0B57)
+GLENUM(GL_FOG, 0x0B60)
+GLENUM(GL_FOG_DENSITY, 0x0B62)
+GLENUM(GL_FOG_START, 0x0B63)
+GLENUM(GL_FOG_END, 0x0B64)
+GLENUM(GL_FOG_MODE, 0x0B65)
+GLENUM(GL_FOG_COLOR, 0x0B66)
+GLENUM(GL_DEPTH_TEST, 0x0B71)
+GLENUM(GL_STENCIL_TEST, 0x0B90)
+GLENUM(GL_NORMALIZE, 0x0BA1)
+GLENUM(GL_ALPHA_TEST, 0x0BC0)
+GLENUM(GL_DITHER, 0x0BD0)
+GLENUM(GL_BLEND, 0x0BE2)
+GLENUM(GL_COLOR_LOGIC_OP, 0x0BF2)
+GLENUM(GL_SCISSOR_TEST, 0x0C11)
+GLENUM(GL_PERSPECTIVE_CORRECTION_HINT, 0x0C50)
+GLENUM(GL_POINT_SMOOTH_HINT, 0x0C51)
+GLENUM(GL_LINE_SMOOTH_HINT, 0x0C52)
+GLENUM(GL_POLYGON_SMOOTH_HINT, 0x0C53)
+GLENUM(GL_FOG_HINT, 0x0C54)
+GLENUM(GL_UNPACK_ALIGNMENT, 0x0CF5)
+GLENUM(GL_PACK_ALIGNMENT, 0x0D05)
+GLENUM(GL_MAX_LIGHTS, 0x0D31)
+GLENUM(GL_MAX_CLIP_PLANES, 0x0D32)
+GLENUM(GL_MAX_TEXTURE_SIZE, 0x0D33)
+GLENUM(GL_MAX_MODELVIEW_STACK_DEPTH, 0x0D36)
+GLENUM(GL_MAX_PROJECTION_STACK_DEPTH, 0x0D38)
+GLENUM(GL_MAX_TEXTURE_STACK_DEPTH, 0x0D39)
+GLENUM(GL_MAX_VIEWPORT_DIMS, 0x0D3A)
+GLENUM(GL_RED_BITS, 0x0D52)
+GLENUM(GL_GREEN_BITS, 0x0D53)
+GLENUM(GL_BLUE_BITS, 0x0D54)
+GLENUM(GL_ALPHA_BITS, 0x0D55)
+GLENUM(GL_DEPTH_BITS, 0x0D56)
+GLENUM(GL_STENCIL_BITS, 0x0D57)
+GLENUM(GL_TEXTURE_2D, 0x0DE1)
+GLENUM(GL_DONT_CARE, 0x1100)
+GLENUM(GL_FASTEST, 0x1101)
+GLENUM(GL_NICEST, 0x1102)
+GLENUM(GL_AMBIENT, 0x1200)
+GLENUM(GL_DIFFUSE, 0x1201)
+GLENUM(GL_SPECULAR, 0x1202)
+GLENUM(GL_POSITION, 0x1203)
+GLENUM(GL_SPOT_DIRECTION, 0x1204)
+GLENUM(GL_SPOT_EXPONENT, 0x1205)
+GLENUM(GL_SPOT_CUTOFF, 0x1206)
+GLENUM(GL_CONSTANT_ATTENUATION, 0x1207)
+GLENUM(GL_LINEAR_ATTENUATION, 0x1208)
+GLENUM(GL_QUADRATIC_ATTENUATION, 0x1209)
+GLENUM(GL_BYTE, 0x1400)
+GLENUM(GL_UNSIGNED_BYTE, 0x1401)
+GLENUM(GL_SHORT, 0x1402)
+GLENUM(GL_UNSIGNED_SHORT, 0x1403)
+GLENUM(GL_FLOAT, 0x1406)
+GLENUM(GL_FIXED, 0x140C)
+GLENUM(GL_CLEAR, 0x1500)
+GLENUM(GL_AND, 0x1501)
+GLENUM(GL_AND_REVERSE, 0x1502)
+GLENUM(GL_COPY, 0x1503)
+GLENUM(GL_AND_INVERTED, 0x1504)
+GLENUM(GL_NOOP, 0x1505)
+GLENUM(GL_XOR, 0x1506)
+GLENUM(GL_OR, 0x1507)
+GLENUM(GL_NOR, 0x1508)
+GLENUM(GL_EQUIV, 0x1509)
+GLENUM(GL_INVERT, 0x150A)
+GLENUM(GL_OR_REVERSE, 0x150B)
+GLENUM(GL_COPY_INVERTED, 0x150C)
+GLENUM(GL_OR_INVERTED, 0x150D)
+GLENUM(GL_NAND, 0x150E)
+GLENUM(GL_SET, 0x150F)
+GLENUM(GL_EMISSION, 0x1600)
+GLENUM(GL_SHININESS, 0x1601)
+GLENUM(GL_AMBIENT_AND_DIFFUSE, 0x1602)
+GLENUM(GL_MODELVIEW, 0x1700)
+GLENUM(GL_PROJECTION, 0x1701)
+GLENUM(GL_TEXTURE, 0x1702)
+GLENUM(GL_ALPHA, 0x1906)
+GLENUM(GL_RGB, 0x1907)
+GLENUM(GL_RGBA, 0x1908)
+GLENUM(GL_LUMINANCE, 0x1909)
+GLENUM(GL_LUMINANCE_ALPHA, 0x190A)
+GLENUM(GL_FLAT, 0x1D00)
+GLENUM(GL_SMOOTH, 0x1D01)
+GLENUM(GL_KEEP, 0x1E00)
+GLENUM(GL_REPLACE, 0x1E01)
+GLENUM(GL_REPLACE, 0x1E01)
+GLENUM(GL_INCR, 0x1E02)
+GLENUM(GL_DECR, 0x1E03)
+GLENUM(GL_VENDOR, 0x1F00)
+GLENUM(GL_RENDERER, 0x1F01)
+GLENUM(GL_VERSION, 0x1F02)
+GLENUM(GL_EXTENSIONS, 0x1F03)
+GLENUM(GL_MODULATE, 0x2100)
+GLENUM(GL_DECAL, 0x2101)
+GLENUM(GL_TEXTURE_ENV_MODE, 0x2200)
+GLENUM(GL_TEXTURE_ENV_COLOR, 0x2201)
+GLENUM(GL_TEXTURE_ENV, 0x2300)
+GLENUM(GL_NEAREST, 0x2600)
+GLENUM(GL_LINEAR, 0x2601)
+GLENUM(GL_NEAREST_MIPMAP_NEAREST, 0x2700)
+GLENUM(GL_LINEAR_MIPMAP_NEAREST, 0x2701)
+GLENUM(GL_NEAREST_MIPMAP_LINEAR, 0x2702)
+GLENUM(GL_LINEAR_MIPMAP_LINEAR, 0x2703)
+GLENUM(GL_TEXTURE_MAG_FILTER, 0x2800)
+GLENUM(GL_TEXTURE_MIN_FILTER, 0x2801)
+GLENUM(GL_TEXTURE_WRAP_S, 0x2802)
+GLENUM(GL_TEXTURE_WRAP_T, 0x2803)
+GLENUM(GL_CLAMP, 0x2900)
+GLENUM(GL_REPEAT, 0x2901)
+GLENUM(GL_CLIP_PLANE0, 0x3000)
+GLENUM(GL_CLIP_PLANE1, 0x3001)
+GLENUM(GL_CLIP_PLANE2, 0x3002)
+GLENUM(GL_CLIP_PLANE3, 0x3003)
+GLENUM(GL_CLIP_PLANE4, 0x3004)
+GLENUM(GL_CLIP_PLANE5, 0x3005)
+GLENUM(GL_LIGHT0, 0x4000)
+GLENUM(GL_LIGHT1, 0x4001)
+GLENUM(GL_LIGHT2, 0x4002)
+GLENUM(GL_LIGHT3, 0x4003)
+GLENUM(GL_LIGHT4, 0x4004)
+GLENUM(GL_LIGHT5, 0x4005)
+GLENUM(GL_LIGHT6, 0x4006)
+GLENUM(GL_LIGHT7, 0x4007)
+GLENUM(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0x7E80)
+GLENUM(GL_UNSIGNED_SHORT_4_4_4_4, 0x8033)
+GLENUM(GL_UNSIGNED_SHORT_5_5_5_1, 0x8034)
+GLENUM(GL_POLYGON_OFFSET_FILL, 0x8037)
+GLENUM(GL_RESCALE_NORMAL, 0x803A)
+GLENUM(GL_VERTEX_ARRAY, 0x8074)
+GLENUM(GL_NORMAL_ARRAY, 0x8075)
+GLENUM(GL_COLOR_ARRAY, 0x8076)
+GLENUM(GL_TEXTURE_COORD_ARRAY, 0x8078)
+GLENUM(GL_MULTISAMPLE, 0x809D)
+GLENUM(GL_SAMPLE_ALPHA_TO_COVERAGE, 0x809E)
+GLENUM(GL_SAMPLE_ALPHA_TO_ONE, 0x809F)
+GLENUM(GL_SAMPLE_COVERAGE, 0x80A0)
+GLENUM(GL_MAX_ELEMENTS_VERTICES, 0x80E8)
+GLENUM(GL_MAX_ELEMENTS_INDICES, 0x80E9)
+GLENUM(GL_CLAMP_TO_EDGE, 0x812F)
+GLENUM(GL_GENERATE_MIPMAP, 0x8191)
+GLENUM(GL_GENERATE_MIPMAP_HINT, 0x8192)
+GLENUM(GL_UNSIGNED_SHORT_5_6_5, 0x8363)
+GLENUM(GL_ALIASED_POINT_SIZE_RANGE, 0x846D)
+GLENUM(GL_ALIASED_LINE_WIDTH_RANGE, 0x846E)
+GLENUM(GL_TEXTURE0, 0x84C0)
+GLENUM(GL_TEXTURE1, 0x84C1)
+GLENUM(GL_TEXTURE2, 0x84C2)
+GLENUM(GL_TEXTURE3, 0x84C3)
+GLENUM(GL_TEXTURE4, 0x84C4)
+GLENUM(GL_TEXTURE5, 0x84C5)
+GLENUM(GL_TEXTURE6, 0x84C6)
+GLENUM(GL_TEXTURE7, 0x84C7)
+GLENUM(GL_TEXTURE8, 0x84C8)
+GLENUM(GL_TEXTURE9, 0x84C9)
+GLENUM(GL_TEXTURE10, 0x84CA)
+GLENUM(GL_TEXTURE11, 0x84CB)
+GLENUM(GL_TEXTURE12, 0x84CC)
+GLENUM(GL_TEXTURE13, 0x84CD)
+GLENUM(GL_TEXTURE14, 0x84CE)
+GLENUM(GL_TEXTURE15, 0x84CF)
+GLENUM(GL_TEXTURE16, 0x84D0)
+GLENUM(GL_TEXTURE17, 0x84D1)
+GLENUM(GL_TEXTURE18, 0x84D2)
+GLENUM(GL_TEXTURE19, 0x84D3)
+GLENUM(GL_TEXTURE20, 0x84D4)
+GLENUM(GL_TEXTURE21, 0x84D5)
+GLENUM(GL_TEXTURE22, 0x84D6)
+GLENUM(GL_TEXTURE23, 0x84D7)
+GLENUM(GL_TEXTURE24, 0x84D8)
+GLENUM(GL_TEXTURE25, 0x84D9)
+GLENUM(GL_TEXTURE26, 0x84DA)
+GLENUM(GL_TEXTURE27, 0x84DB)
+GLENUM(GL_TEXTURE28, 0x84DC)
+GLENUM(GL_TEXTURE29, 0x84DD)
+GLENUM(GL_TEXTURE30, 0x84DE)
+GLENUM(GL_TEXTURE31, 0x84DF)
+GLENUM(GL_MAX_TEXTURE_UNITS, 0x84E2)
+GLENUM(GL_NUM_COMPRESSED_TEXTURE_FORMATS, 0x86A2)
+GLENUM(GL_COMPRESSED_TEXTURE_FORMATS, 0x86A3)
+GLENUM(GL_BUFFER_SIZE, 0x8764)
+GLENUM(GL_BUFFER_USAGE, 0x8765)
+GLENUM(GL_POINT_SPRITE_OES, 0x8861)
+GLENUM(GL_COORD_REPLACE_OES, 0x8862)
+GLENUM(GL_ARRAY_BUFFER, 0x8892)
+GLENUM(GL_ELEMENT_ARRAY_BUFFER, 0x8893)
+GLENUM(GL_ARRAY_BUFFER_BINDING, 0x8894)
+GLENUM(GL_ELEMENT_ARRAY_BUFFER_BINDING, 0x8895)
+GLENUM(GL_VERTEX_ARRAY_BUFFER_BINDING, 0x8896)
+GLENUM(GL_NORMAL_ARRAY_BUFFER_BINDING, 0x8897)
+GLENUM(GL_COLOR_ARRAY_BUFFER_BINDING, 0x8898)
+GLENUM(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, 0x889A)
+GLENUM(GL_STATIC_DRAW, 0x88E4)
+GLENUM(GL_DYNAMIC_DRAW, 0x88E8)
+GLENUM(GL_POINT_SIZE_ARRAY_TYPE_OES, 0x898A)
+GLENUM(GL_POINT_SIZE_ARRAY_STRIDE_OES, 0x898B)
+GLENUM(GL_POINT_SIZE_ARRAY_POINTER_OES, 0x898C)
+GLENUM(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898D)
+GLENUM(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898E)
+GLENUM(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898F)
+GLENUM(GL_PALETTE4_RGB8_OES, 0x8B90)
+GLENUM(GL_PALETTE4_RGBA8_OES, 0x8B91)
+GLENUM(GL_PALETTE4_R5_G6_B5_OES, 0x8B92)
+GLENUM(GL_PALETTE4_RGBA4_OES, 0x8B93)
+GLENUM(GL_PALETTE4_RGB5_A1_OES, 0x8B94)
+GLENUM(GL_PALETTE8_RGB8_OES, 0x8B95)
+GLENUM(GL_PALETTE8_RGBA8_OES, 0x8B96)
+GLENUM(GL_PALETTE8_R5_G6_B5_OES, 0x8B97)
+GLENUM(GL_PALETTE8_RGBA4_OES, 0x8B98)
+GLENUM(GL_PALETTE8_RGB5_A1_OES, 0x8B99)
+GLENUM(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, 0x8B9A)
+GLENUM(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, 0x8B9B)
+GLENUM(GL_POINT_SIZE_ARRAY_OES, 0x8B9C)
+GLENUM(GL_TEXTURE_CROP_RECT_OES, 0x8B9D)
+GLENUM(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES, 0x8B9F)
diff --git a/opengl/libGLES_CM/gl_logger.cpp b/opengl/libGLES_CM/gl_logger.cpp
new file mode 100644
index 0000000..14b5a39
--- /dev/null
+++ b/opengl/libGLES_CM/gl_logger.cpp
@@ -0,0 +1,1059 @@
+/*
+ ** Copyright 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.
+ */
+
+#define LOG_TAG "GLLogger"
+
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <dlfcn.h>
+
+#include <sys/ioctl.h>
+
+#include <GLES/egl.h>
+
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+
+#include <utils/String8.h>
+
+#include "gl_logger.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#undef NELEM
+#define NELEM(x) (sizeof(x)/sizeof(*(x)))
+
+template<typename T>
+static int binarySearch(T const sortedArray[], int first, int last, EGLint key)
+{
+   while (first <= last) {
+       int mid = (first + last) / 2;
+       if (key > sortedArray[mid].key) {
+           first = mid + 1;
+       } else if (key < sortedArray[mid].key) {
+           last = mid - 1;
+       } else {
+           return mid;
+       }
+   }
+   return -1;
+}
+
+struct pair_t {
+    const char* name;
+    int         key;
+};
+
+static const pair_t gEnumMap[] = {
+    #define GLENUM(NAME, VALUE) { #NAME, VALUE },
+    #include "gl_enums.in"
+    #undef GLENUM
+};
+
+// ----------------------------------------------------------------------------
+
+template<typename TYPE>
+class GLLogValue {
+public:
+    GLLogValue(TYPE value) : mValue(value) { }
+    const TYPE& getValue() const { return mValue; }
+    String8 toString() const {
+        return convertToString(mValue);
+    }
+private:
+    const TYPE& mValue;
+    String8 convertToString(unsigned int v) const {
+        char buf[16];
+        snprintf(buf, 16, "%u", v);
+        return String8(buf);
+    }
+    String8 convertToString(unsigned long v) const {
+        char buf[16];
+        snprintf(buf, 16, "%lu", v);
+        return String8(buf);
+    }
+    String8 convertToString(int v) const {
+        char buf[16];
+        snprintf(buf, 16, "%d", v);
+        return String8(buf);
+    }
+    String8 convertToString(long v) const {
+        char buf[16];
+        snprintf(buf, 16, "%ld", v);
+        return String8(buf);
+    }
+    String8 convertToString(float v) const {
+        char buf[16];
+        snprintf(buf, 16, "%f", v);
+        return String8(buf);
+    }
+    String8 convertToString(void const* v) const {
+        char buf[16];
+        snprintf(buf, 16, "%p", v);
+        return String8(buf);
+    }
+};
+
+class GLLogEnum : public GLLogValue<GLenum> {
+public:
+    GLLogEnum(GLenum v) : GLLogValue<GLenum>(v) { }
+    String8 toString() const {
+        GLenum v = getValue();
+        int i = binarySearch<pair_t>(gEnumMap, 0, NELEM(gEnumMap)-1, v);
+        if (i >= 0) {
+            return String8(gEnumMap[i].name);
+        } else {
+            char buf[16];
+            snprintf(buf, 16, "0x%04x", v);
+            return String8(buf);
+        }
+    }
+};
+
+class GLLogClearBitfield : public GLLogValue<GLbitfield> {
+public:
+    GLLogClearBitfield(GLbitfield v) : GLLogValue<GLbitfield>(v) { }
+    String8 toString() const {
+        char buf[16];
+        snprintf(buf, 16, "0x%08x", getValue());
+        return String8(buf);
+    }
+};
+
+class GLLogBool : public GLLogValue<GLboolean> {
+public:
+    GLLogBool(GLboolean v) : GLLogValue<GLboolean>(v) { }
+    String8 toString() const {
+        GLboolean v = getValue();
+        if (v == GL_TRUE)   return String8("GL_TRUE");
+        if (v == GL_FALSE)  return String8("GL_FALSE");
+        return GLLogValue<GLboolean>::toString();
+    }
+};
+
+class GLLogFixed : public GLLogValue<GLfixed> {
+public:
+    GLLogFixed(GLfixed v) : GLLogValue<GLfixed>(v) { }
+    String8 toString() const {
+        char buf[16];
+        snprintf(buf, 16, "0x%08x", getValue());
+        return String8(buf);
+    }
+};
+
+
+template <typename TYPE>
+class GLLogBuffer : public GLLogValue<TYPE *> {
+public:
+    GLLogBuffer(TYPE* buffer, size_t count = -1)
+        : GLLogValue<TYPE*>(buffer)
+    { // output buffer
+    }
+    GLLogBuffer(TYPE const* buffer, size_t count = -1)
+    : GLLogValue<TYPE*>(const_cast<TYPE*>(buffer))
+    { // input buffer
+    }
+};
+
+class GLLog
+{
+public:
+    GLLog(const char* name) : mNumParams(0) {
+        mString.append(name);
+        mString.append("(");
+    }
+
+    ~GLLog() {
+        LOGD("%s);", mString.string());
+    }
+
+    GLLog& operator << (unsigned char v) {
+        return *this << GLLogValue<unsigned int>(v);
+    }
+    GLLog& operator << (short v) {
+        return *this << GLLogValue<unsigned int>(v);
+    }
+    GLLog& operator << (unsigned int v) {
+        return *this << GLLogValue<unsigned int>(v);
+    }
+    GLLog& operator << (int v) {
+        return *this << GLLogValue<int>(v);
+    }
+    GLLog& operator << (long v) {
+        return *this << GLLogValue<long>(v);
+    }
+    GLLog& operator << (unsigned long v) {
+        return *this << GLLogValue<unsigned long>(v);
+    }
+    GLLog& operator << (float v) {
+        return *this << GLLogValue<float>(v);
+    }
+    GLLog& operator << (const void* v) {
+        return *this << GLLogValue<const void* >(v);
+    }
+
+    template <typename TYPE>
+    GLLog& operator << (const TYPE& rhs) {
+        if (mNumParams > 0)
+            mString.append(", ");
+        mString.append(rhs.toString());
+        mNumParams++;
+        return *this;
+    }
+
+    const String8& string() const { return mString; }
+private:
+    GLLog(const GLLog&);
+
+    String8 mString;
+    int mNumParams;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+#define API_ENTRY(api)                      log_##api
+#define CALL_GL_API(_x, ...)
+#define CALL_GL_API_RETURN(_x, ...)         return(0);
+
+void API_ENTRY(glActiveTexture)(GLenum texture) {
+    CALL_GL_API(glActiveTexture, texture);
+    GLLog("glActiveTexture") << GLLogEnum(texture);
+}
+
+void API_ENTRY(glAlphaFunc)(GLenum func, GLclampf ref) {
+    CALL_GL_API(glAlphaFunc, func, ref);
+    GLLog("glAlphaFunc") << GLLogEnum(func) << ref;
+}
+
+void API_ENTRY(glAlphaFuncx)(GLenum func, GLclampx ref) {
+    CALL_GL_API(glAlphaFuncx, func, ref);
+    GLLog("glAlphaFuncx") << GLLogEnum(func) << GLLogFixed(ref);
+}
+
+void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) {
+    CALL_GL_API(glBindTexture, target, texture);
+    GLLog("glBindTexture") << GLLogEnum(target) << texture;
+}
+
+void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) {
+    CALL_GL_API(glBlendFunc, sfactor, dfactor);
+    GLLog("glBlendFunc") << GLLogEnum(sfactor) << GLLogEnum(dfactor);
+}
+
+void API_ENTRY(glClear)(GLbitfield mask) {
+    CALL_GL_API(glClear, mask);
+    GLLog("glClear") << GLLogClearBitfield(mask);
+}
+
+void API_ENTRY(glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
+    CALL_GL_API(glClearColor, red, green, blue, alpha);
+    GLLog("glClearColor") << red << green << blue << alpha;
+}
+
+void API_ENTRY(glClearColorx)(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) {
+    CALL_GL_API(glClearColorx, red, green, blue, alpha);
+    GLLog("glClearColorx") << GLLogFixed(red) << GLLogFixed(green) << GLLogFixed(blue) << GLLogFixed(alpha);
+}
+
+void API_ENTRY(glClearDepthf)(GLclampf depth) {
+    CALL_GL_API(glClearDepthf, depth);
+    GLLog("glClearDepthf") << depth;
+}
+
+void API_ENTRY(glClearDepthx)(GLclampx depth) {
+    CALL_GL_API(glClearDepthx, depth);
+    GLLog("glClearDepthx") << GLLogFixed(depth);
+}
+
+void API_ENTRY(glClearStencil)(GLint s) {
+    CALL_GL_API(glClearStencil, s);
+    GLLog("glClearStencil") << s;
+}
+
+void API_ENTRY(glClientActiveTexture)(GLenum texture) {
+    CALL_GL_API(glClientActiveTexture, texture);
+    GLLog("glClientActiveTexture") << GLLogEnum(texture);
+}
+
+void API_ENTRY(glColor4f)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+    CALL_GL_API(glColor4f, red, green, blue, alpha);
+    GLLog("glColor4f") << red << green << blue << alpha;
+}
+
+void API_ENTRY(glColor4x)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) {
+    CALL_GL_API(glColor4x, red, green, blue, alpha);
+    GLLog("glColor4x") << GLLogFixed(red) << GLLogFixed(green) << GLLogFixed(blue) << GLLogFixed(alpha);
+}
+
+void API_ENTRY(glColorMask)(GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+    CALL_GL_API(glColorMask, r, g, b, a);
+    GLLog("glColorMask") << GLLogBool(r) << GLLogBool(g) << GLLogBool(b) << GLLogBool(a);
+}
+
+void API_ENTRY(glColorPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr)
+{
+    CALL_GL_API(glColorPointer, size, type, stride, ptr);
+    GLLog("glColorPointer") << size << GLLogEnum(type) << stride << ptr;
+}
+
+void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat,
+                            GLsizei width, GLsizei height, GLint border,
+                            GLsizei imageSize, const GLvoid *data) {
+    CALL_GL_API(glCompressedTexImage2D, target, level, internalformat,
+            width, height, border, imageSize, data);
+    GLLog("glCompressedTexImage2D")
+                << GLLogEnum(target) << level << GLLogEnum(internalformat)
+                << width << height << border << imageSize << data;
+}
+
+void API_ENTRY(glCompressedTexSubImage2D)( GLenum target, GLint level, GLint xoffset,
+                                GLint yoffset, GLsizei width, GLsizei height,
+                                GLenum format, GLsizei imageSize,
+                                const GLvoid *data) {
+    CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset,
+            width, height, format, imageSize, data);
+    GLLog("glCompressedTexSubImage2D")
+            << GLLogEnum(target) << level << xoffset << yoffset
+            << width << height << GLLogEnum(format) << imageSize << data;
+}
+
+void API_ENTRY(glCopyTexImage2D)(  GLenum target, GLint level, GLenum internalformat,
+                        GLint x, GLint y, GLsizei width, GLsizei height,
+                        GLint border) {
+    CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y,
+            width, height, border);
+    GLLog("glCopyTexImage2D")
+            << GLLogEnum(target) << level << GLLogEnum(internalformat)
+            << x << y << width << height << border;
+}
+
+void API_ENTRY(glCopyTexSubImage2D)(   GLenum target, GLint level, GLint xoffset,
+                            GLint yoffset, GLint x, GLint y, GLsizei width,
+                            GLsizei height) {
+    CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y,
+            width, height);
+    GLLog("glCopyTexSubImage2D")
+            << GLLogEnum(target) << level << xoffset << yoffset
+            << x << y << width << height;
+}
+
+void API_ENTRY(glCullFace)(GLenum mode) {
+    CALL_GL_API(glCullFace, mode);
+    GLLog("glCullFace") << GLLogEnum(mode);
+}
+
+void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) {
+    CALL_GL_API(glDeleteTextures, n, textures);
+    GLLog("glDeleteTextures") << n << GLLogBuffer<GLuint>(textures, n);
+}
+
+void API_ENTRY(glDepthFunc)(GLenum func) {
+    CALL_GL_API(glDepthFunc, func);
+    GLLog("glDepthFunc") << GLLogEnum(func);
+}
+
+void API_ENTRY(glDepthMask)(GLboolean flag) {
+    CALL_GL_API(glDepthMask, flag);
+    GLLog("glDepthMask") << GLLogBool(flag);
+}
+
+void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) {
+    CALL_GL_API(glDepthRangef, zNear, zFar);
+    GLLog("glDepthRangef") << zNear << zFar;
+}
+
+void API_ENTRY(glDepthRangex)(GLclampx zNear, GLclampx zFar) {
+    CALL_GL_API(glDepthRangex, zNear, zFar);
+    GLLog("glDepthRangex") << GLLogFixed(zNear) << GLLogFixed(zFar);
+}
+
+void API_ENTRY(glDisable)(GLenum cap) {
+    CALL_GL_API(glDisable, cap);
+    GLLog("glDisable") << GLLogEnum(cap);
+}
+
+void API_ENTRY(glDisableClientState)(GLenum array) {
+    CALL_GL_API(glDisableClientState, array);
+    GLLog("glDisableClientState") << GLLogEnum(array);
+}
+
+void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) {
+    CALL_GL_API(glDrawArrays, mode, first, count);
+    GLLog("glDrawArrays") << GLLogEnum(mode) << first << count;
+}
+
+void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count,
+                    GLenum type, const GLvoid *indices) {
+    CALL_GL_API(glDrawElements, mode, count, type, indices);
+    GLLog log("glDrawElements");
+    log << GLLogEnum(mode) << count << GLLogEnum(type);
+    if (type == GL_UNSIGNED_BYTE) {
+        log << GLLogBuffer<GLubyte>(static_cast<const GLubyte*>(indices), count);
+    } else {
+        log << GLLogBuffer<GLushort>(static_cast<const GLushort*>(indices), count);
+    }
+    log;
+}
+
+void API_ENTRY(glEnable)(GLenum cap) {
+    CALL_GL_API(glEnable, cap);
+    GLLog("glEnable") << GLLogEnum(cap);
+}
+
+void API_ENTRY(glEnableClientState)(GLenum array) {
+    CALL_GL_API(glEnableClientState, array);
+    GLLog("glEnableClientState") << GLLogEnum(array);
+}
+
+void API_ENTRY(glFinish)(void) {
+    CALL_GL_API(glFinish);
+    GLLog("glFinish");
+}
+
+void API_ENTRY(glFlush)(void) {
+    CALL_GL_API(glFlush);
+    GLLog("glFlush");
+}
+
+void API_ENTRY(glFogf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glFogf, pname, param);
+    GLLog("glFogf") << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glFogfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glFogfv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glFogfv") << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glFogx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glFogx, pname, param);
+    GLLog("glFogx") << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glFogxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glFogxv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glFogfx") << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+
+void API_ENTRY(glFrontFace)(GLenum mode) {
+    CALL_GL_API(glFrontFace, mode);
+    GLLog("glFrontFace") << GLLogEnum(mode);
+ }
+
+void API_ENTRY(glFrustumf)(GLfloat left, GLfloat right,
+                GLfloat bottom, GLfloat top,
+                GLfloat zNear, GLfloat zFar) {
+    CALL_GL_API(glFrustumf, left, right, bottom, top, zNear, zFar);
+    GLLog("glFrustumf") << left << right << bottom << top << zNear << zFar;
+}
+
+void API_ENTRY(glFrustumx)(GLfixed left, GLfixed right,
+                GLfixed bottom, GLfixed top,
+                GLfixed zNear, GLfixed zFar) {
+    CALL_GL_API(glFrustumx, left, right, bottom, top, zNear, zFar);
+    GLLog("glFrustumx")
+            << GLLogFixed(left) << GLLogFixed(right)
+            << GLLogFixed(bottom) << GLLogFixed(top)
+            << GLLogFixed(zNear) << GLLogFixed(zFar);
+}
+
+void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) {
+    CALL_GL_API(glGenTextures, n, textures);
+    GLLog("glGenTextures") << n << GLLogBuffer<GLuint>(textures, n);
+}
+
+GLenum API_ENTRY(glGetError)(void) {
+    GLLog("glGetError");
+    CALL_GL_API_RETURN(glGetError);
+}
+
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *params) {
+    CALL_GL_API(glGetIntegerv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetIntegerv") << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+
+const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+    GLLog("glGetString") << GLLogEnum(name);
+    CALL_GL_API_RETURN(glGetString, name);
+}
+
+void API_ENTRY(glHint)(GLenum target, GLenum mode) {
+    CALL_GL_API(glHint, target, mode);
+    GLLog("GLenum") << GLLogEnum(target) << GLLogEnum(mode);
+}
+
+void API_ENTRY(glLightModelf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glLightModelf, pname, param);
+    GLLog("glLightModelf") << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glLightModelfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glLightModelfv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glLightModelfv") << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glLightModelx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glLightModelx, pname, param);
+    GLLog("glLightModelx") << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glLightModelxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glLightModelxv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glLightModelxv") << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+
+void API_ENTRY(glLightf)(GLenum light, GLenum pname, GLfloat param) {
+    CALL_GL_API(glLightf, light, pname, param);
+    GLLog("glLightf") << GLLogEnum(light) << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glLightfv)(GLenum light, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glLightfv, light, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glLightfv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glLightx)(GLenum light, GLenum pname, GLfixed param) {
+   CALL_GL_API(glLightx, light, pname, param);
+   GLLog("glLightx") << GLLogEnum(light) << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glLightxv)(GLenum light, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glLightxv, light, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glLightxv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+
+void API_ENTRY(glLineWidth)(GLfloat width) {
+    CALL_GL_API(glLineWidth, width);
+    GLLog("glLineWidth") << width;
+}
+
+void API_ENTRY(glLineWidthx)(GLfixed width) {
+    CALL_GL_API(glLineWidthx, width);
+    GLLog("glLineWidth") << GLLogFixed(width);
+}
+
+void API_ENTRY(glLoadIdentity)(void) {
+    CALL_GL_API(glLoadIdentity);
+    GLLog("glLoadIdentity");
+}
+
+void API_ENTRY(glLoadMatrixf)(const GLfloat *m) {
+    CALL_GL_API(glLoadMatrixf, m);
+    GLLog("glLoadMatrixf") << GLLogBuffer<GLfloat>(m, 16);
+}
+
+void API_ENTRY(glLoadMatrixx)(const GLfixed *m) {
+    CALL_GL_API(glLoadMatrixx, m);
+    GLLog("glLoadMatrixx") << GLLogBuffer<GLfixed>(m, 16);
+}
+
+void API_ENTRY(glLogicOp)(GLenum opcode) {
+    CALL_GL_API(glLogicOp, opcode);
+    GLLog("glLogicOp") << GLLogEnum(opcode);
+}
+
+void API_ENTRY(glMaterialf)(GLenum face, GLenum pname, GLfloat param) {
+    CALL_GL_API(glMaterialf, face, pname, param);
+    GLLog("glMaterialf") << GLLogEnum(face) << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glMaterialfv)(GLenum face, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glMaterialfv, face, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glMaterialfv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glMaterialx)(GLenum face, GLenum pname, GLfixed param) {
+    CALL_GL_API(glMaterialx, face, pname, param);
+    GLLog("glMaterialx") << GLLogEnum(face) << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glMaterialxv)(GLenum face, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glMaterialxv, face, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glMaterialxv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+
+void API_ENTRY(glMatrixMode)(GLenum mode) {
+    CALL_GL_API(glMatrixMode, mode);
+    GLLog("glMatrixMode") << GLLogEnum(mode);
+}
+
+void API_ENTRY(glMultMatrixf)(const GLfloat *m) {
+    CALL_GL_API(glMultMatrixf, m);
+    GLLog("glMultMatrixf") << GLLogBuffer<GLfloat>(m, 16);
+}
+
+void API_ENTRY(glMultMatrixx)(const GLfixed *m) {
+    CALL_GL_API(glMultMatrixx, m);
+    GLLog("glMultMatrixx") << GLLogBuffer<GLfixed>(m, 16);
+}
+
+void API_ENTRY(glMultiTexCoord4f)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) {
+    CALL_GL_API(glMultiTexCoord4f, target, s, t, r, q);
+    GLLog("glMultiTexCoord4f") << GLLogEnum(target) << s << t << r << q;
+}
+
+void API_ENTRY(glMultiTexCoord4x)(GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) {
+    CALL_GL_API(glMultiTexCoord4x, target, s, t, r, q);
+    GLLog("glMultiTexCoord4x") << GLLogEnum(target)
+        << GLLogFixed(s) << GLLogFixed(t) << GLLogFixed(r) << GLLogFixed(q);
+}
+
+void API_ENTRY(glNormal3f)(GLfloat nx, GLfloat ny, GLfloat nz) {
+    CALL_GL_API(glNormal3f, nx, ny, nz);
+    GLLog("glNormal3f") << nx << ny << nz;
+}
+
+void API_ENTRY(glNormal3x)(GLfixed nx, GLfixed ny, GLfixed nz) {
+    CALL_GL_API(glNormal3x, nx, ny, nz);
+    GLLog("glNormal3x") << GLLogFixed(nx) << GLLogFixed(ny) << GLLogFixed(nz);
+}
+
+void API_ENTRY(glNormalPointer)(GLenum type, GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glNormalPointer, type, stride, pointer);
+    GLLog("glNormalPointer") << GLLogEnum(type) << stride << pointer;
+}
+
+void API_ENTRY(glOrthof)(  GLfloat left, GLfloat right,
+                GLfloat bottom, GLfloat top,
+                GLfloat zNear, GLfloat zFar) {
+    CALL_GL_API(glOrthof, left, right, bottom, top, zNear, zFar);
+    GLLog("glOrthof") << left << right << bottom << top << zNear << zFar;
+}
+
+void API_ENTRY(glOrthox)(  GLfixed left, GLfixed right,
+                GLfixed bottom, GLfixed top,
+                GLfixed zNear, GLfixed zFar) {
+    CALL_GL_API(glOrthox, left, right, bottom, top, zNear, zFar);
+    GLLog("glOrthox") << GLLogFixed(left) << GLLogFixed(right)
+            << GLLogFixed(bottom) << GLLogFixed(top)
+            << GLLogFixed(zNear) << GLLogFixed(zFar);
+}
+
+void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) {
+    CALL_GL_API(glPixelStorei, pname, param);
+    GLLog("glPixelStorei") << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glPointSize)(GLfloat size) {
+    CALL_GL_API(glPointSize, size);
+    GLLog("glPointSize") << size;
+}
+
+void API_ENTRY(glPointSizex)(GLfixed size) {
+    CALL_GL_API(glPointSizex, size);
+    GLLog("glPointSizex") << GLLogFixed(size);
+}
+
+void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) {
+    CALL_GL_API(glPolygonOffset, factor, units);
+    GLLog("glPolygonOffset") << factor << units;
+}
+
+void API_ENTRY(glPolygonOffsetx)(GLfixed factor, GLfixed units) {
+    CALL_GL_API(glPolygonOffsetx, factor, units);
+    GLLog("glPolygonOffsetx") << GLLogFixed(factor) << GLLogFixed(units);
+}
+
+void API_ENTRY(glPopMatrix)(void) {
+    CALL_GL_API(glPopMatrix);
+    GLLog("glPopMatrix");
+}
+
+void API_ENTRY(glPushMatrix)(void) {
+    CALL_GL_API(glPushMatrix);
+    GLLog("glPushMatrix");
+}
+
+void API_ENTRY(glReadPixels)(  GLint x, GLint y, GLsizei width, GLsizei height,
+                    GLenum format, GLenum type, GLvoid *pixels) {
+    CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glReadPixels") << x << y << width << height << GLLogEnum(format) << GLLogEnum(type)
+            << GLLogBuffer<unsigned char>(static_cast<unsigned char *>(pixels));
+}
+
+void API_ENTRY(glRotatef)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glRotatef, angle, x, y, z);
+    GLLog("glRotatef") << angle << x << y << z;
+}
+
+void API_ENTRY(glRotatex)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glRotatex, angle, x, y, z);
+    GLLog("glRotatex") << GLLogFixed(angle) << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z);
+}
+
+void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) {
+    CALL_GL_API(glSampleCoverage, value, invert);
+    GLLog("glSampleCoverage") << value << GLLogBool(invert);
+}
+
+void API_ENTRY(glSampleCoveragex)(GLclampx value, GLboolean invert) {
+    CALL_GL_API(glSampleCoveragex, value, invert);
+    GLLog("glSampleCoveragex") << GLLogFixed(value) << GLLogBool(invert);
+}
+
+void API_ENTRY(glScalef)(GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glScalef, x, y, z);
+    GLLog("glScalef") << x << y << z;
+}
+
+void API_ENTRY(glScalex)(GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glScalex, x, y, z);
+    GLLog("glScalex") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z);
+}
+
+void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) {
+    CALL_GL_API(glScissor, x, y, width, height);
+    GLLog("glScissor") << x << y << width << height;
+}
+
+void API_ENTRY(glShadeModel)(GLenum mode) {
+    CALL_GL_API(glShadeModel, mode);
+    GLLog("glShadeModel") << GLLogEnum(mode);
+}
+
+void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
+    CALL_GL_API(glStencilFunc, func, ref, mask);
+    GLLog("glStencilFunc") << GLLogEnum(func) << ref << mask;
+}
+
+void API_ENTRY(glStencilMask)(GLuint mask) {
+    CALL_GL_API(glStencilMask, mask);
+    GLLog("glStencilMask") << mask;
+}
+
+void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) {
+    CALL_GL_API(glStencilOp, fail, zfail, zpass);
+    GLLog("glStencilOp") << GLLogEnum(fail) << GLLogEnum(zfail) << GLLogEnum(zpass);
+}
+
+void API_ENTRY(glTexCoordPointer)( GLint size, GLenum type,
+                        GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glTexCoordPointer, size, type, stride, pointer);
+    GLLog("glTexCoordPointer") << size << GLLogEnum(type) << stride << pointer;
+}
+
+void API_ENTRY(glTexEnvf)(GLenum target, GLenum pname, GLfloat param) {
+    CALL_GL_API(glTexEnvf, target, pname, param);
+    GLLog("glTexEnvf") << GLLogEnum(target) << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glTexEnvfv)(GLenum target, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glTexEnvfv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexEnvx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glTexEnvx)(GLenum target, GLenum pname, GLfixed param) {
+    CALL_GL_API(glTexEnvx, target, pname, param);
+    GLLog("glTexEnvx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glTexEnvxv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexEnvxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+
+void API_ENTRY(glTexImage2D)(  GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLint border, GLenum format,
+                    GLenum type, const GLvoid *pixels) {
+    CALL_GL_API(glTexImage2D, target, level, internalformat, width, height,
+            border, format, type, pixels);
+    GLLog("glTexImage2D") << GLLogEnum(target) << level << GLLogEnum(internalformat)
+            << width << height << border << GLLogEnum(format) << GLLogEnum(type)
+            << GLLogBuffer<unsigned char>( static_cast<const unsigned char *>(pixels));
+}
+
+void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
+    CALL_GL_API(glTexParameterf, target, pname, param);
+    GLLog("glTexParameterf") << GLLogEnum(target) << GLLogEnum(pname) << param;
+}
+
+void API_ENTRY(glTexParameterx)(GLenum target, GLenum pname, GLfixed param) {
+    CALL_GL_API(glTexParameterx, target, pname, param);
+    GLLog("glTexParameterx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogFixed(param);
+}
+
+void API_ENTRY(glTexSubImage2D)(   GLenum target, GLint level, GLint xoffset,
+                        GLint yoffset, GLsizei width, GLsizei height,
+                        GLenum format, GLenum type, const GLvoid *pixels) {
+    CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset,
+            width, height, format, type, pixels);
+    GLLog("glTexSubImage2D") << GLLogEnum(target) << level << xoffset << yoffset
+            << width << height << GLLogEnum(format) << GLLogEnum(type)
+            << GLLogBuffer<unsigned char>( static_cast<const unsigned char *>(pixels));
+}
+
+void API_ENTRY(glTranslatef)(GLfloat x, GLfloat y, GLfloat z) {
+    CALL_GL_API(glTranslatef, x, y, z);
+    GLLog("glTranslatef") << x << y << z;
+}
+
+void API_ENTRY(glTranslatex)(GLfixed x, GLfixed y, GLfixed z) {
+    CALL_GL_API(glTranslatex, x, y, z);
+    GLLog("glTranslatex") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z);
+}
+
+void API_ENTRY(glVertexPointer)(   GLint size, GLenum type,
+                        GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glVertexPointer, size, type, stride, pointer);
+    GLLog("glVertexPointer") << size << GLLogEnum(type) << stride << pointer;
+}
+
+void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) {
+    CALL_GL_API(glViewport, x, y, width, height);
+    GLLog("glViewport") << x << y << width << height;
+}
+
+// ES 1.1
+void API_ENTRY(glClipPlanef)(GLenum plane, const GLfloat *equation) {
+    CALL_GL_API(glClipPlanef, plane, equation);
+    GLLog("glClipPlanef") << GLLogEnum(plane) << GLLogBuffer<GLfloat>(equation, 4);
+}
+void API_ENTRY(glClipPlanex)(GLenum plane, const GLfixed *equation) {
+    CALL_GL_API(glClipPlanex, plane, equation);
+    GLLog("glClipPlanex") << GLLogEnum(plane) << GLLogBuffer<GLfixed>(equation, 4);
+}
+void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) {
+    CALL_GL_API(glBindBuffer, target, buffer);
+    GLLog("glBindBuffer") << GLLogEnum(target) << buffer;
+}
+void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
+    CALL_GL_API(glBufferData, target, size, data, usage);
+    GLLog("glBufferData") << GLLogEnum(target) << size
+        << GLLogBuffer<unsigned char>(static_cast<const unsigned char*>(data), size);
+}
+void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) {
+    CALL_GL_API(glBufferSubData, target, offset, size, data);
+    GLLog("glBufferSubData") << GLLogEnum(target) << offset << size
+        << GLLogBuffer<unsigned char>(static_cast<const unsigned char*>(data), size);
+}
+void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint* buffers) {
+    CALL_GL_API(glDeleteBuffers, n, buffers);
+    GLLog("glDeleteBuffers") << n << GLLogBuffer<GLuint>(buffers, n);
+}
+void API_ENTRY(glGenBuffers)(GLsizei n, GLuint* buffers) {
+    CALL_GL_API(glGenBuffers, n, buffers);
+    GLLog("glGenBuffers") << n << GLLogBuffer<GLuint>(buffers, n);
+}
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *params) {
+    CALL_GL_API(glGetBooleanv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetBooleanv") << GLLogEnum(pname) << GLLogBuffer<GLboolean>(params);
+}
+void API_ENTRY(glGetFixedv)(GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetFixedv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetFixedv") << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetFloatv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetFloatv") << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glGetPointerv)(GLenum pname, void **params) {
+    CALL_GL_API(glGetPointerv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetPointerv") << GLLogEnum(pname) << GLLogBuffer<void*>(params);
+}
+void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
+    // XXX: we need to compute the size of this buffer
+    CALL_GL_API(glGetBufferParameteriv, target, pname, params);
+    GLLog("glGetBufferParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+void API_ENTRY(glGetClipPlanef)(GLenum pname, GLfloat eqn[4]) {
+    CALL_GL_API(glGetClipPlanef, pname, eqn);
+    GLLog("glGetClipPlanef") << GLLogEnum(pname) << GLLogBuffer<GLfloat>(eqn, 4);
+}
+void API_ENTRY(glGetClipPlanex)(GLenum pname, GLfixed eqn[4]) {
+    CALL_GL_API(glGetClipPlanex, pname, eqn);
+    GLLog("glGetClipPlanex") << GLLogEnum(pname) << GLLogBuffer<GLfixed>(eqn, 4);
+}
+void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetLightxv, light, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetLightxv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glGetLightfv)(GLenum light, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetLightfv, light, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetLightfv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetMaterialxv, face, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetMaterialxv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glGetMaterialfv)(GLenum face, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetMaterialfv, face, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetMaterialfv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glGetTexEnvfv)(GLenum env, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetTexEnvfv, env, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexEnvfv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) {
+    CALL_GL_API(glGetTexEnviv, env, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexEnviv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+void API_ENTRY(glGetTexEnvxv)(GLenum env, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetTexEnvxv, env, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexEnvxv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) {
+    CALL_GL_API(glGetTexParameterfv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexParameterfv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) {
+    CALL_GL_API(glGetTexParameteriv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+void API_ENTRY(glGetTexParameterxv)(GLenum target, GLenum pname, GLfixed *params) {
+    CALL_GL_API(glGetTexParameterxv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glGetTexParameterxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) {
+    GLLog("glIsBuffer") << buffer;
+    CALL_GL_API_RETURN(glIsBuffer, buffer);
+}
+GLboolean API_ENTRY(glIsEnabled)(GLenum cap) {
+    GLLog("glIsEnabled") << GLLogEnum(cap);
+    CALL_GL_API_RETURN(glIsEnabled, cap);
+}
+GLboolean API_ENTRY(glIsTexture)(GLuint texture) {
+    GLLog("glIsTexture") << texture;
+    CALL_GL_API_RETURN(glIsTexture, texture);
+}
+void API_ENTRY(glPointParameterf)(GLenum pname, GLfloat param) {
+    CALL_GL_API(glPointParameterf, pname, param);
+    GLLog("glPointParameterf") << GLLogEnum(pname) << param;
+}
+void API_ENTRY(glPointParameterfv)(GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glPointParameterfv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glPointParameterfv") << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+void API_ENTRY(glPointParameterx)(GLenum pname, GLfixed param) {
+    CALL_GL_API(glPointParameterx, pname, param);
+    GLLog("glPointParameterx") << GLLogEnum(pname) << GLLogFixed(param);
+}
+void API_ENTRY(glPointParameterxv)(GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glPointParameterxv, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glPointParameterxv") << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glColor4ub)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) {
+    CALL_GL_API(glColor4ub, red, green, blue, alpha);
+    GLLog("glColor4ub") << red << green << blue << alpha;
+}
+void API_ENTRY(glTexEnvi)(GLenum target, GLenum pname, GLint param) {
+    CALL_GL_API(glTexEnvi, target, pname, param);
+    GLLog("glTexEnvi") << GLLogEnum(target) << GLLogEnum(pname) << param;
+}
+void API_ENTRY(glTexEnviv)(GLenum target, GLenum pname, const GLint *params) {
+    CALL_GL_API(glTexEnviv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexEnviv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+
+void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) {
+    CALL_GL_API(glTexParameterfv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexParameterfv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfloat>(params);
+}
+
+void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) {
+    CALL_GL_API(glTexParameteriv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLint>(params);
+}
+
+void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) {
+    CALL_GL_API(glTexParameteri, target, pname, param);
+    GLLog("glTexParameteri") << GLLogEnum(target) << GLLogEnum(pname) << param;
+}
+void API_ENTRY(glTexParameterxv)(GLenum target, GLenum pname, const GLfixed *params) {
+    CALL_GL_API(glTexParameterxv, target, pname, params);
+    // XXX: we need to compute the size of this buffer
+    GLLog("glTexParameterxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer<GLfixed>(params);
+}
+void API_ENTRY(glPointSizePointerOES)(GLenum type, GLsizei stride, const GLvoid *pointer) {
+    CALL_GL_API(glPointSizePointerOES, type, stride, pointer);
+    GLLog("glPointSizePointerOES") << GLLogEnum(type) << stride << pointer;
+}
+
+// Extensions
+void API_ENTRY(glDrawTexsOES)(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
+    CALL_GL_API(glDrawTexsOES, x, y, z, w, h);
+    GLLog("glDrawTexsOES") << x << y << z << w << h;
+}
+void API_ENTRY(glDrawTexiOES)(GLint x, GLint y, GLint z, GLint w, GLint h) {
+    CALL_GL_API(glDrawTexiOES, x, y, z, w, h);
+    GLLog("glDrawTexiOES") << x << y << z << w << h;
+}
+void API_ENTRY(glDrawTexfOES)(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h) {
+    CALL_GL_API(glDrawTexfOES, x, y, z, w, h);
+    GLLog("glDrawTexfOES") << x << y << z << w << h;
+}
+void API_ENTRY(glDrawTexxOES)(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
+    CALL_GL_API(glDrawTexxOES, x, y, z, w, h);
+    GLLog("glDrawTexfOES") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z) << GLLogFixed(w) << GLLogFixed(h);
+}
+void API_ENTRY(glDrawTexsvOES)(const GLshort* coords) {
+    CALL_GL_API(glDrawTexsvOES, coords);
+    GLLog("glDrawTexsvOES") << GLLogBuffer<GLshort>(coords, 5);
+}
+void API_ENTRY(glDrawTexivOES)(const GLint* coords) {
+    CALL_GL_API(glDrawTexivOES, coords);
+    GLLog("glDrawTexivOES") << GLLogBuffer<GLint>(coords, 5);
+}
+void API_ENTRY(glDrawTexfvOES)(const GLfloat* coords) {
+    CALL_GL_API(glDrawTexfvOES, coords);
+    GLLog("glDrawTexfvOES") << GLLogBuffer<GLfloat>(coords, 5);
+}
+void API_ENTRY(glDrawTexxvOES)(const GLfixed* coords) {
+    CALL_GL_API(glDrawTexxvOES, coords);
+    GLLog("glDrawTexxvOES") << GLLogBuffer<GLfixed>(coords, 5);
+}
+GLbitfield API_ENTRY(glQueryMatrixxOES)(GLfixed* mantissa, GLint* exponent) {
+    GLLog("glQueryMatrixxOES") << GLLogBuffer<GLfixed>(mantissa, 16) << GLLogBuffer<GLfixed>(exponent, 16);
+    CALL_GL_API_RETURN(glQueryMatrixxOES, mantissa, exponent);
+}
diff --git a/opengl/libGLES_CM/gl_logger.h b/opengl/libGLES_CM/gl_logger.h
new file mode 100644
index 0000000..59e31c7
--- /dev/null
+++ b/opengl/libGLES_CM/gl_logger.h
@@ -0,0 +1,26 @@
+/* 
+ ** Copyright 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.
+ */
+
+#ifndef ANDROID_GL_LOGGER_H
+#define ANDROID_GL_LOGGER_H
+
+extern "C" {
+#define GL_ENTRY(r, api, ...) r log_##api(__VA_ARGS__);
+#include "gl_entries.cpp"
+#undef GL_ENTRY
+};
+
+#endif /* ANDROID_GL_LOGGER_H */
diff --git a/opengl/libGLES_CM/gl_wrapper.cpp b/opengl/libGLES_CM/gl_wrapper.cpp
new file mode 100644
index 0000000..5da4f9a
--- /dev/null
+++ b/opengl/libGLES_CM/gl_wrapper.cpp
@@ -0,0 +1,1663 @@
+/* 
+ ** Copyright 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.
+ */
+
+#define LOG_TAG "GLLogger"
+
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <dlfcn.h>
+
+#include <sys/ioctl.h>
+
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#endif
+
+#include <GLES/egl.h>
+
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+#include <cutils/memory.h>
+
+#include <utils/IMemory.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/IServiceManager.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Parcel.h>
+
+#include <ui/EGLDisplaySurface.h>
+#include <ui/ISurfaceComposer.h>
+
+#include "gl_logger.h"
+
+#undef NELEM
+
+#define GL_LOGGER                   0
+#define USE_SLOW_BINDING            0
+#define NELEM(x)                    (sizeof(x)/sizeof(*(x)))
+#define MAX_NUMBER_OF_GL_EXTENSIONS 32
+#define MAKE_CONFIG(_impl, _index)  ((EGLConfig)(((_impl)<<24) | (_index)))
+#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+//  EGLDisplay are global, not attached to a given thread
+static const unsigned int NUM_DISPLAYS = 1;
+static const unsigned int IMPL_HARDWARE                 = 0;
+static const unsigned int IMPL_SOFTWARE                 = 1;
+static const unsigned int IMPL_HARDWARE_CONTEXT_LOST    = 2;
+static const unsigned int IMPL_SOFTWARE_CONTEXT_LOST    = 3;
+static const unsigned int IMPL_NO_CONTEXT               = 4;
+
+// ----------------------------------------------------------------------------
+
+struct gl_hooks_t;
+
+struct egl_connection_t
+{
+    void volatile *     dso;
+    gl_hooks_t *        hooks;
+    EGLint              major;
+    EGLint              minor;
+    int                 unavailable;
+};
+
+template <int MAGIC>
+struct egl_object_t
+{
+    egl_object_t() : magic(MAGIC) { }
+    ~egl_object_t() { magic = 0; }
+    bool isValid() const { return magic == MAGIC; }
+private:
+    uint32_t    magic;
+};
+
+struct egl_display_t : public egl_object_t<'_dpy'>
+{
+    EGLDisplay  dpys[2];
+    EGLConfig*  configs[2];
+    EGLint      numConfigs[2];
+    EGLint      numTotalConfigs;
+    char const* extensionsString;
+    volatile int32_t refs;
+    struct strings_t {
+        char const * vendor;
+        char const * version;
+        char const * clientApi;
+        char const * extensions;
+        char const * extensions_config;
+    };
+    strings_t   queryString[2];
+};
+
+struct egl_surface_t : public egl_object_t<'_srf'>
+{
+    egl_surface_t(EGLDisplay dpy, EGLSurface surface,
+            NativeWindowType window, int impl, egl_connection_t const* cnx) 
+    : dpy(dpy), surface(surface), window(window), impl(impl), cnx(cnx)
+    {
+        // NOTE: window must be incRef'ed and connected already
+    }
+    ~egl_surface_t() {
+        if (window) {
+            if (window->disconnect)
+                window->disconnect(window);
+            window->decRef(window);
+        }
+    }
+    EGLDisplay                  dpy;
+    EGLSurface                  surface;
+    NativeWindowType            window;
+    int                         impl;
+    egl_connection_t const*     cnx;
+};
+
+struct egl_context_t : public egl_object_t<'_ctx'>
+{
+    egl_context_t(EGLDisplay dpy, EGLContext context,
+            int impl, egl_connection_t const* cnx) 
+    : dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx)
+    {
+    }
+    EGLDisplay                  dpy;
+    EGLContext                  context;
+    EGLSurface                  read;
+    EGLSurface                  draw;
+    int                         impl;
+    egl_connection_t const*     cnx;
+};
+
+struct tls_t
+{
+    tls_t() : error(EGL_SUCCESS), ctx(0) { }
+    EGLint      error;
+    EGLContext  ctx;
+};
+
+
+// GL / EGL hooks
+
+typedef void(*proc_t)();
+
+struct gl_hooks_t {
+    struct gl_t {
+        #define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
+        #include "gl_entries.cpp"
+        #undef GL_ENTRY
+    } gl;
+    struct egl_t {
+        #define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
+        #include "egl_entries.cpp"
+        #undef EGL_ENTRY
+    } egl;
+    struct gl_ext_t {
+        void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);
+    } ext;
+};
+
+static char const * const gl_names[] = {
+    #define GL_ENTRY(_r, _api, ...) #_api,
+    #include "gl_entries.cpp"
+    #undef GL_ENTRY
+    NULL
+};
+
+static char const * const egl_names[] = {
+    #define EGL_ENTRY(_r, _api, ...) #_api,
+    #include "egl_entries.cpp"
+    #undef EGL_ENTRY
+    NULL
+};
+
+static void gl_unimplemented() {
+    LOGE("called unimplemented OpenGL ES API");
+}
+
+// ----------------------------------------------------------------------------
+
+static egl_connection_t gEGLImpl[2];
+static egl_display_t gDisplay[NUM_DISPLAYS];
+static gl_hooks_t gHooks[5];
+static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_key_t gEGLThreadLocalStorageKey = -1;
+
+// ----------------------------------------------------------------------------
+
+#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && !GL_LOGGER
+
+#include <sys/tls.h>
+// We have a dedicated TLS slot in bionic
+static inline void setGlThreadSpecific(gl_hooks_t const *value) {
+    ((uint32_t *)__get_tls())[TLS_SLOT_OPENGL_API] = (uint32_t)value;
+}
+static gl_hooks_t const* getGlThreadSpecific() {
+    gl_hooks_t const* hooks = (gl_hooks_t const *)(((unsigned const *)__get_tls())[TLS_SLOT_OPENGL_API]);
+    if (hooks) return hooks;
+    return &gHooks[IMPL_NO_CONTEXT];
+}
+
+#else
+
+static pthread_key_t gGLWrapperKey = -1;
+static inline void setGlThreadSpecific(gl_hooks_t const *value) {
+    pthread_setspecific(gGLWrapperKey, value);
+}
+static gl_hooks_t const* getGlThreadSpecific() {
+    gl_hooks_t const* hooks =  static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey));
+    if (hooks) return hooks;
+    return &gHooks[IMPL_NO_CONTEXT];
+}
+
+#endif
+
+static __attribute__((noinline))
+const char *egl_strerror(EGLint err)
+{
+    switch (err){
+        case EGL_SUCCESS:               return "EGL_SUCCESS";
+        case EGL_NOT_INITIALIZED:       return "EGL_NOT_INITIALIZED";
+        case EGL_BAD_ACCESS:            return "EGL_BAD_ACCESS";
+        case EGL_BAD_ALLOC:             return "EGL_BAD_ALLOC";
+        case EGL_BAD_ATTRIBUTE:         return "EGL_BAD_ATTRIBUTE";
+        case EGL_BAD_CONFIG:            return "EGL_BAD_CONFIG";
+        case EGL_BAD_CONTEXT:           return "EGL_BAD_CONTEXT";
+        case EGL_BAD_CURRENT_SURFACE:   return "EGL_BAD_CURRENT_SURFACE";
+        case EGL_BAD_DISPLAY:           return "EGL_BAD_DISPLAY";
+        case EGL_BAD_MATCH:             return "EGL_BAD_MATCH";
+        case EGL_BAD_NATIVE_PIXMAP:     return "EGL_BAD_NATIVE_PIXMAP";
+        case EGL_BAD_NATIVE_WINDOW:     return "EGL_BAD_NATIVE_WINDOW";
+        case EGL_BAD_PARAMETER:         return "EGL_BAD_PARAMETER";
+        case EGL_BAD_SURFACE:           return "EGL_BAD_SURFACE";
+        case EGL_CONTEXT_LOST:          return "EGL_CONTEXT_LOST";
+        default: return "UNKNOWN";
+    }
+}
+
+static __attribute__((noinline))
+void clearTLS() {
+    if (gEGLThreadLocalStorageKey != -1) {
+        tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
+        if (tls) {
+            delete tls;
+            pthread_setspecific(gEGLThreadLocalStorageKey, 0);
+        }
+    }
+}
+
+static tls_t* getTLS()
+{
+    tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
+    if (tls == 0) {
+        tls = new tls_t;
+        pthread_setspecific(gEGLThreadLocalStorageKey, tls);
+    }
+    return tls;
+}
+
+template<typename T>
+static __attribute__((noinline))
+T setErrorEtc(const char* caller, int line, EGLint error, T returnValue) {
+    if (gEGLThreadLocalStorageKey == -1) {
+        pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
+        if (gEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&gEGLThreadLocalStorageKey, NULL);
+        pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
+    }
+    tls_t* tls = getTLS();
+    if (tls->error != error) {
+        LOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
+        tls->error = error;
+    }
+    return returnValue;
+}
+
+static __attribute__((noinline))
+GLint getError() {
+    if (gEGLThreadLocalStorageKey == -1)
+        return EGL_SUCCESS;
+    tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
+    if (!tls) return EGL_SUCCESS;
+    GLint error = tls->error;
+    tls->error = EGL_SUCCESS;
+    return error;
+}
+
+static __attribute__((noinline))
+void setContext(EGLContext ctx) {
+    if (gEGLThreadLocalStorageKey == -1) {
+        pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
+        if (gEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&gEGLThreadLocalStorageKey, NULL);
+        pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
+    }
+    tls_t* tls = getTLS();
+    tls->ctx = ctx;
+}
+
+static __attribute__((noinline))
+EGLContext getContext() {
+    if (gEGLThreadLocalStorageKey == -1)
+        return EGL_NO_CONTEXT;
+    tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
+    if (!tls) return EGL_NO_CONTEXT;
+    return tls->ctx;
+}
+
+/*****************************************************************************/
+
+/*
+ * we provide our own allocators for the GPU regions, these
+ * allocators go through surfaceflinger 
+ */
+
+static Mutex                            gRegionsLock;
+static request_gpu_t                    gRegions;
+static sp<ISurfaceComposer>             gSurfaceManager;
+ISurfaceComposer*                       GLES_localSurfaceManager = 0;
+
+const sp<ISurfaceComposer>& getSurfaceFlinger()
+{
+    Mutex::Autolock _l(gRegionsLock);
+
+    /*
+     * There is a little bit of voodoo magic here. We want to access
+     * surfaceflinger for allocating GPU regions, however, when we are
+     * running as part of surfaceflinger, we want to bypass the
+     * service manager because surfaceflinger might not be registered yet.
+     * SurfaceFlinger will populate "GLES_localSurfaceManager" with its
+     * own address, so we can just use that.
+     */
+    if (gSurfaceManager == 0) {
+        if (GLES_localSurfaceManager) {
+            // we're running in SurfaceFlinger's context
+            gSurfaceManager =  GLES_localSurfaceManager;
+        } else {
+            // we're a remote process or not part of surfaceflinger,
+            // go through the service manager
+            sp<IServiceManager> sm = defaultServiceManager();
+            if (sm != NULL) {
+                sp<IBinder> binder = sm->getService(String16("SurfaceFlinger"));
+                gSurfaceManager = interface_cast<ISurfaceComposer>(binder);
+            }
+        }
+    }
+    return gSurfaceManager;
+}
+
+class GPURevokeRequester : public BnGPUCallback
+{
+public:
+    virtual void gpuLost() {
+        LOGD("CONTEXT_LOST: Releasing GPU upon request from SurfaceFlinger.");
+        gEGLImpl[IMPL_HARDWARE].hooks = &gHooks[IMPL_HARDWARE_CONTEXT_LOST];
+    }
+};
+
+static sp<GPURevokeRequester> gRevokerCallback;
+
+static request_gpu_t* gpu_acquire(void* user)
+{
+    sp<ISurfaceComposer> server( getSurfaceFlinger() );
+
+    Mutex::Autolock _l(gRegionsLock);
+    if (server == NULL) {
+        return 0;
+    }
+    
+    ISurfaceComposer::gpu_info_t info;
+    gRevokerCallback = new GPURevokeRequester();
+    status_t err = server->requestGPU(gRevokerCallback, &info);
+    if (err != NO_ERROR) {
+        LOGD("requestGPU returned %d", err);
+        return 0;
+    }
+
+    bool failed = false;
+    request_gpu_t* gpu = &gRegions;
+    memset(gpu, 0, sizeof(*gpu));
+    
+    if (info.regs != 0) {
+        sp<IMemoryHeap> heap(info.regs->getMemory());
+        if (heap != 0) {
+            int fd = heap->heapID();
+            gpu->regs.fd = fd;
+            gpu->regs.base = info.regs->pointer(); 
+            gpu->regs.size = info.regs->size(); 
+            gpu->regs.user = info.regs.get();
+#if HAVE_ANDROID_OS
+            struct pmem_region region;
+            if (ioctl(fd, PMEM_GET_PHYS, &region) >= 0)
+                gpu->regs.phys = (void*)region.offset;
+#endif
+            info.regs->incStrong(gpu);
+        } else {
+            LOGE("GPU register handle %p is invalid!", info.regs.get());
+            failed = true;
+        }
+    }
+
+    for (size_t i=0 ; i<info.count && !failed ; i++) {
+        sp<IMemory>& region(info.regions[i].region);
+        if (region != 0) {
+            sp<IMemoryHeap> heap(region->getMemory());
+            if (heap != 0) {
+                const int fd = heap->heapID();
+                gpu->gpu[i].fd = fd;
+                gpu->gpu[i].base = region->pointer(); 
+                gpu->gpu[i].size = region->size(); 
+                gpu->gpu[i].user = region.get();
+                gpu->gpu[i].offset = info.regions[i].reserved;
+#if HAVE_ANDROID_OS
+                struct pmem_region reg;
+                if (ioctl(fd, PMEM_GET_PHYS, &reg) >= 0)
+                    gpu->gpu[i].phys = (void*)reg.offset;
+#endif
+                region->incStrong(gpu);
+            } else {
+                LOGE("GPU region handle [%d, %p] is invalid!", i, region.get());
+                failed = true;
+            }
+        }
+    }
+    
+    if (failed) {
+        // something went wrong, clean up everything!
+        if (gpu->regs.user) {
+            static_cast<IMemory*>(gpu->regs.user)->decStrong(gpu);
+            for (size_t i=0 ; i<info.count ; i++) {
+                if (gpu->gpu[i].user) {
+                    static_cast<IMemory*>(gpu->gpu[i].user)->decStrong(gpu);
+                }
+            }
+        }
+    }
+    
+    gpu->count = info.count;
+    return gpu;
+}
+
+static int gpu_release(void*, request_gpu_t* gpu)
+{
+    sp<IMemory> regs;
+
+    { // scope for lock
+        Mutex::Autolock _l(gRegionsLock);
+        regs = static_cast<IMemory*>(gpu->regs.user);   
+        gpu->regs.user = 0;
+        if (regs != 0) regs->decStrong(gpu);
+        
+        for (int i=0 ; i<gpu->count ; i++) {
+            sp<IMemory> r(static_cast<IMemory*>(gpu->gpu[i].user));
+            gpu->gpu[i].user = 0;
+            if (r != 0) r->decStrong(gpu);
+        }
+    }
+    
+    // there is a special transaction to relinquish the GPU
+    // (it will happen automatically anyway if we don't do this)
+    Parcel data, reply;
+    // NOTE: this transaction does not require an interface token
+    regs->asBinder()->transact(1000, data, &reply);
+    return 1;
+}
+
+/*****************************************************************************/
+
+static __attribute__((noinline))
+void *load_driver(const char* driver, gl_hooks_t* hooks)
+{
+    void* dso = dlopen(driver, RTLD_NOW | RTLD_LOCAL);
+    LOGE_IF(!dso,
+            "couldn't load <%s> library (%s)",
+            driver, dlerror());
+
+    if (dso) {
+        void** curr;
+        char const * const * api;
+        gl_hooks_t::gl_t* gl = &hooks->gl;
+        curr = (void**)gl;
+        api = gl_names;
+        while (*api) {
+            void* f = dlsym(dso, *api);
+            //LOGD("<%s> @ 0x%p", *api, f);
+            if (f == NULL) {
+                //LOGW("<%s> not found in %s", *api, driver);
+                f = (void*)gl_unimplemented;
+            }
+            *curr++ = f;
+            api++;
+        }
+        gl_hooks_t::egl_t* egl = &hooks->egl;
+        curr = (void**)egl;
+        api = egl_names;
+        while (*api) {
+            void* f = dlsym(dso, *api);
+            if (f == NULL) {
+                //LOGW("<%s> not found in %s", *api, driver);
+                f = (void*)0;
+            }
+            *curr++ = f;
+            api++;
+        }
+
+        // hook this driver up with surfaceflinger if needed
+        register_gpu_t register_gpu = 
+            (register_gpu_t)dlsym(dso, "oem_register_gpu");
+
+        if (register_gpu != NULL) {
+            if (getSurfaceFlinger() != 0) {
+                register_gpu(dso, gpu_acquire, gpu_release);
+            }
+        }
+    }
+    return dso;
+}
+
+template<typename T>
+static __attribute__((noinline))
+int binarySearch(
+        T const sortedArray[], int first, int last, T key)
+{
+    while (first <= last) {
+        int mid = (first + last) / 2;
+        if (key > sortedArray[mid]) { 
+            first = mid + 1;
+        } else if (key < sortedArray[mid]) { 
+            last = mid - 1;
+        } else {
+            return mid;
+        }
+    }
+    return -1;
+}
+
+static int cmp_configs(const void* a, const void *b)
+{
+    EGLConfig c0 = *(EGLConfig const *)a;
+    EGLConfig c1 = *(EGLConfig const *)b;
+    return c0<c1 ? -1 : (c0>c1 ? 1 : 0);
+}
+
+static char const * const gVendorString     = "Android";
+static char const * const gVersionString    = "1.2 Android META-EGL";
+static char const * const gClientApiString  = "OpenGL ES";
+
+struct extention_map_t {
+    const char* name;
+    void (*address)(void);
+};
+
+static const extention_map_t gExtentionMap[] = {
+    { "eglSwapRectangleANDROID",         (void(*)())&eglSwapRectangleANDROID },
+    { "eglQueryStringConfigANDROID",     (void(*)())&eglQueryStringConfigANDROID },
+};
+
+static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS];
+
+static void(*findProcAddress(const char* name,
+        const extention_map_t* map, size_t n))() 
+{
+    for (uint32_t i=0 ; i<n ; i++) {
+        if (!strcmp(name, map[i].name)) {
+            return map[i].address;
+        }
+    }
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+
+// ----------------------------------------------------------------------------
+// extensions for the framework
+// ----------------------------------------------------------------------------
+
+void glColorPointerBounds(GLint size, GLenum type, GLsizei stride,
+        const GLvoid *ptr, GLsizei count) {
+    glColorPointer(size, type, stride, ptr);
+}
+void glNormalPointerBounds(GLenum type, GLsizei stride,
+        const GLvoid *pointer, GLsizei count) {
+    glNormalPointer(type, stride, pointer);
+}
+void glTexCoordPointerBounds(GLint size, GLenum type,
+        GLsizei stride, const GLvoid *pointer, GLsizei count) {
+    glTexCoordPointer(size, type, stride, pointer);
+}
+void glVertexPointerBounds(GLint size, GLenum type,
+        GLsizei stride, const GLvoid *pointer, GLsizei count) {
+    glVertexPointer(size, type, stride, pointer);
+}
+
+
+// ----------------------------------------------------------------------------
+// Actual GL wrappers
+// ----------------------------------------------------------------------------
+
+#if __OPTIMIZE__ && defined(__arm__) && !defined(__thumb__) && !USE_SLOW_BINDING && !GL_LOGGER
+
+    #define API_ENTRY(_api) __attribute__((naked)) _api
+    #define CALL_GL_API(_api, ...)                              \
+         asm volatile(                                          \
+            "mov   r12, #0xFFFF0FFF   \n"                       \
+            "ldr   r12, [r12, #-15]   \n"                       \
+            "ldr   r12, [r12, %[tls]] \n"                       \
+            "cmp   r12, #0            \n"                       \
+            "ldrne pc,  [r12, %[api]] \n"                       \
+            "bx    lr                 \n"                       \
+            :                                                   \
+            : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
+              [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api))    \
+            :                                                   \
+            );
+    
+    #define CALL_GL_API_RETURN(_api, ...) \
+        CALL_GL_API(_api, __VA_ARGS__) \
+        return 0; // placate gcc's warnings. never reached.
+
+#else
+
+    #define API_ENTRY(_api) _api
+    #if GL_LOGGER
+
+        #define CALL_GL_API(_api, ...)          \
+            gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+            log_##_api(__VA_ARGS__); \
+            _c->_api(__VA_ARGS__);
+        
+        #define CALL_GL_API_RETURN(_api, ...)   \
+            gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+            log_##_api(__VA_ARGS__); \
+            return _c->_api(__VA_ARGS__)
+
+    #else
+
+        #define CALL_GL_API(_api, ...)          \
+            gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+            _c->_api(__VA_ARGS__);
+        
+        #define CALL_GL_API_RETURN(_api, ...)   \
+            gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+            return _c->_api(__VA_ARGS__)
+
+    #endif
+
+#endif
+
+#include "gl_api.cpp"
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+static int gl_context_lost() {
+    setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]);
+    return 0;
+}
+static int egl_context_lost() {
+    setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]);
+    return EGL_FALSE;
+}
+static EGLBoolean egl_context_lost_swap_buffers(void*, void*) {
+    usleep(100000); // don't use all the CPU
+    setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]);
+    return EGL_FALSE;
+}
+static GLint egl_context_lost_get_error() {
+    return EGL_CONTEXT_LOST;
+}
+static int ext_context_lost() {
+    return 0;
+}
+
+static void gl_no_context() {
+    LOGE("call to OpenGL ES API with no current context");
+}
+static void early_egl_init(void) 
+{
+#if !defined(HAVE_ANDROID_OS) || USE_SLOW_BINDING || GL_LOGGER
+    pthread_key_create(&gGLWrapperKey, NULL);
+#endif
+    uint32_t addr = (uint32_t)((void*)gl_no_context);
+    android_memset32((uint32_t*)(void*)&gHooks[IMPL_NO_CONTEXT], addr, sizeof(gHooks[IMPL_NO_CONTEXT]));
+    setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]);
+}
+
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
+
+
+static inline
+egl_display_t* get_display(EGLDisplay dpy)
+{
+    uintptr_t index = uintptr_t(dpy)-1U;
+    return (index >= NUM_DISPLAYS) ? NULL : &gDisplay[index];
+}
+
+static inline
+egl_surface_t* get_surface(EGLSurface surface)
+{
+    egl_surface_t* s = (egl_surface_t *)surface;
+    return s;
+}
+
+static inline
+egl_context_t* get_context(EGLContext context)
+{
+    egl_context_t* c = (egl_context_t *)context;
+    return c;
+}
+
+static egl_connection_t* validate_display_config(
+        EGLDisplay dpy, EGLConfig config,
+        egl_display_t const*& dp, int& impl, int& index)
+{
+    dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (egl_connection_t*)NULL);
+
+    impl = uintptr_t(config)>>24;
+    if (uint32_t(impl) >= 2) {
+        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
+    } 
+    index = uintptr_t(config) & 0xFFFFFF;
+    if (index >= dp->numConfigs[impl]) {
+        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
+    }
+    egl_connection_t* const cnx = &gEGLImpl[impl];
+    if (cnx->dso == 0) {
+        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
+    }
+    return cnx;
+}
+
+static EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx)
+{
+    if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!get_display(dpy)->isValid())
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!ctx) // TODO: make sure context is a valid object
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!get_context(ctx)->isValid())
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    return EGL_TRUE;
+}
+
+static EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface)
+{
+    if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!get_display(dpy)->isValid())
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!surface) // TODO: make sure surface is a valid object
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    if (!get_surface(surface)->isValid())
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return EGL_TRUE;
+}
+
+static void add_extension(egl_display_t* dp, char const*& p, const char* ext)
+{
+    if (!strstr(p, ext)) {
+        p = (char const*)realloc((void*)p, strlen(p) + 1 + strlen(ext) + 1);
+        strcat((char*)p, " ");
+        strcat((char*)p, ext);
+    }
+    if (!strstr(dp->extensionsString, ext)) {
+        char const*& es = dp->extensionsString;
+        es = (char const*)realloc((void*)es, strlen(es) + 1 + strlen(ext) + 1);
+        strcat((char*)es, " ");
+        strcat((char*)es, ext);
+    }    
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+EGLDisplay eglGetDisplay(NativeDisplayType display)
+{
+    if (sEarlyInitState) {
+        return EGL_NO_DISPLAY;
+    }
+
+    uint32_t index = uint32_t(display);
+    if (index >= NUM_DISPLAYS) {
+        return EGL_NO_DISPLAY;
+    }
+    
+    EGLDisplay dpy = EGLDisplay(uintptr_t(display) + 1LU);
+    egl_display_t* d = &gDisplay[index];
+        
+    // dynamically load all our EGL implementations for that display
+    // and call into the real eglGetGisplay()
+    egl_connection_t* cnx = &gEGLImpl[IMPL_SOFTWARE];
+    if (cnx->dso == 0) {
+        cnx->hooks = &gHooks[IMPL_SOFTWARE];
+        cnx->dso = load_driver("libagl.so", cnx->hooks);
+    }
+    if (cnx->dso && d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY) {
+        d->dpys[IMPL_SOFTWARE] = cnx->hooks->egl.eglGetDisplay(display);
+        LOGE_IF(d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY,
+                "No EGLDisplay for software EGL!");
+    }
+
+    cnx = &gEGLImpl[IMPL_HARDWARE];
+    if (cnx->dso == 0 && cnx->unavailable == 0) {
+        char value[PROPERTY_VALUE_MAX];
+        property_get("debug.egl.hw", value, "1");
+        if (atoi(value) != 0) {
+            cnx->hooks = &gHooks[IMPL_HARDWARE];
+            cnx->dso = load_driver("libhgl.so", cnx->hooks);
+        } else {
+            LOGD("3D hardware acceleration is disabled");
+        }
+    }
+    if (cnx->dso && d->dpys[IMPL_HARDWARE]==EGL_NO_DISPLAY) {
+        android_memset32(
+                (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].gl,
+                (uint32_t)((void*)gl_context_lost),
+                sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].gl));
+        android_memset32(
+                (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl,
+                (uint32_t)((void*)egl_context_lost),
+                sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl));
+        android_memset32(
+                (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].ext,
+                (uint32_t)((void*)ext_context_lost),
+                sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].ext));
+
+        gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglSwapBuffers =
+                egl_context_lost_swap_buffers;
+        
+        gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglGetError =
+                egl_context_lost_get_error;
+
+        gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglTerminate =
+                gHooks[IMPL_HARDWARE].egl.eglTerminate;
+        
+        d->dpys[IMPL_HARDWARE] = cnx->hooks->egl.eglGetDisplay(display);
+        if (d->dpys[IMPL_HARDWARE] == EGL_NO_DISPLAY) {
+            dlclose((void*)cnx->dso);
+            cnx->dso = 0;
+            // in case of failure, we want to make sure we don't try again
+            // as it's expensive.
+            cnx->unavailable = 1;
+        }
+    }
+
+    return dpy;
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+    egl_display_t * const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    if (android_atomic_inc(&dp->refs) > 0) {
+        if (major != NULL) *major = 1;
+        if (minor != NULL) *minor = 2;
+        return EGL_TRUE;
+    }
+    
+    setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]);
+    
+    // initialize each EGL and
+    // build our own extension string first, based on the extension we know
+    // and the extension supported by our client implementation
+    dp->extensionsString = strdup("EGL_ANDROID_query_string_config");
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        cnx->major = -1;
+        cnx->minor = -1;
+        if (cnx->dso && cnx->hooks->egl.eglInitialize(
+                dp->dpys[i], &cnx->major, &cnx->minor)) {
+
+            //LOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p",
+            //        i, dp->dpys[i], cnx->major, cnx->minor, cnx);
+
+            // get the query-strings for this display for each implementation
+            dp->queryString[i].vendor =
+                cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VENDOR);
+            dp->queryString[i].version =
+                cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VERSION);
+            dp->queryString[i].extensions = strdup(
+                cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_EXTENSIONS));
+            dp->queryString[i].clientApi =
+                cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_CLIENT_APIS);
+            
+            // Dynamically insert extensions we know about
+            if (cnx->hooks->egl.eglSwapRectangleANDROID)
+                add_extension(dp, dp->queryString[i].extensions,
+                        "EGL_ANDROID_swap_rectangle");
+
+            if (cnx->hooks->egl.eglQueryStringConfigANDROID)
+                add_extension(dp, dp->queryString[i].extensions,
+                        "EGL_ANDROID_query_string_config");
+        }
+    }
+            
+    // Build the extension list that depends on the current config.
+    // It is the intersection of our extension list and the
+    // underlaying EGL's extensions list
+    EGLBoolean res = EGL_FALSE;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso && cnx->major>=0 && cnx->minor>=0) {
+            char const* const their_extensions = dp->queryString[i].extensions;            
+            char* our_extensions = strdup(dp->extensionsString);
+            char* const our_extensions_org = our_extensions;
+            char* extensions_config = (char*)calloc(strlen(our_extensions)+2, 1);
+            char* p;
+            do {
+                p = strchr(our_extensions, ' ');
+                if (p)  *p++ = 0;
+                else    p = strchr(our_extensions, 0);
+                if (strstr(their_extensions, our_extensions)) {
+                    strcat(extensions_config, our_extensions);
+                    strcat(extensions_config, " ");
+                }
+                our_extensions = p;
+            } while (*p);
+            free((void*)our_extensions_org);
+
+            // remove the trailling white space
+            if (extensions_config[0] != 0) {
+                size_t l = strlen(extensions_config) - 1; // new size
+                extensions_config[l] = 0; // remove the trailling white space
+                extensions_config = (char*)realloc(extensions_config, l+1);
+            } else {
+                extensions_config = (char*)realloc(extensions_config, 1);
+            }
+            dp->queryString[i].extensions_config = extensions_config;
+
+            EGLint n;
+            if (cnx->hooks->egl.eglGetConfigs(dp->dpys[i], 0, 0, &n)) {
+                dp->configs[i] = (EGLConfig*)malloc(sizeof(EGLConfig)*n);
+                if (dp->configs[i]) {
+                    if (cnx->hooks->egl.eglGetConfigs(
+                            dp->dpys[i], dp->configs[i], n, &dp->numConfigs[i]))
+                    {
+                        // sort the configurations so we can do binary searches
+                        qsort(  dp->configs[i],
+                                dp->numConfigs[i],
+                                sizeof(EGLConfig), cmp_configs);
+
+                        dp->numTotalConfigs += n;
+                        res = EGL_TRUE;
+                    }
+                }
+            }
+        }
+    }
+
+    if (res == EGL_TRUE) {
+        if (major != NULL) *major = 1;
+        if (minor != NULL) *minor = 2;
+        return EGL_TRUE;
+    }
+    return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+}
+
+EGLBoolean eglTerminate(EGLDisplay dpy)
+{
+    egl_display_t* const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (android_atomic_dec(&dp->refs) != 1)
+        return EGL_TRUE;
+        
+    EGLBoolean res = EGL_FALSE;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            cnx->hooks->egl.eglTerminate(dp->dpys[i]);
+            
+            /* REVISIT: it's unclear what to do if eglTerminate() fails,
+             * on one end we shouldn't care, on the other end if it fails
+             * it might not be safe to call dlclose() (there could be some
+             * threads around). */
+            
+            free(dp->configs[i]);
+            free((void*)dp->queryString[i].extensions_config);
+            free((void*)dp->queryString[i].extensions);
+            dp->numConfigs[i] = 0;
+            dp->dpys[i] = EGL_NO_DISPLAY;
+            dlclose((void*)cnx->dso);
+            cnx->dso = 0;
+            res = EGL_TRUE;
+        }
+    }
+    free((void*)dp->extensionsString);
+    dp->extensionsString = 0;
+    dp->numTotalConfigs = 0;
+    clearTLS();
+    return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigs(   EGLDisplay dpy,
+                            EGLConfig *configs,
+                            EGLint config_size, EGLint *num_config)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    GLint numConfigs = dp->numTotalConfigs;
+    if (!configs) {
+        *num_config = numConfigs;
+        return EGL_TRUE;
+    }
+    GLint n = 0;
+    for (int j=0 ; j<2 ; j++) {
+        for (int i=0 ; i<dp->numConfigs[j] && config_size ; i++) {
+            *configs++ = MAKE_CONFIG(j, i);
+            config_size--;
+            n++;
+        }
+    }    
+    
+    *num_config = n;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
+                            EGLConfig *configs, EGLint config_size,
+                            EGLint *num_config)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    if (configs == 0) {
+        *num_config = 0;
+        return EGL_TRUE;
+    }
+
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            EGLint n;
+            if (cnx->hooks->egl.eglChooseConfig(
+                    dp->dpys[i], attrib_list, configs, config_size, &n))
+            {
+                // now we need to convert these client EGLConfig to our
+                // internal EGLConfig format. This is done in O(n log n).
+                for (int j=0 ; j<n ; j++) {
+                    int index = binarySearch<EGLConfig>(
+                            dp->configs[i], 0, dp->numConfigs[i]-1, configs[j]);
+                    if (index >= 0) {
+                        configs[j] = MAKE_CONFIG(i, index);
+                    } else {
+                        return setError(EGL_BAD_CONFIG, EGL_FALSE);
+                    }
+                }
+                configs += n;
+                config_size -= n;
+                *num_config += n;
+                res = EGL_TRUE;
+            }
+        }
+    }
+    return res;
+}
+
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
+        EGLint attribute, EGLint *value)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (!cnx) return EGL_FALSE;
+    return cnx->hooks->egl.eglGetConfigAttrib(
+            dp->dpys[i], dp->configs[i][index], attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
+                                    NativeWindowType window,
+                                    const EGLint *attrib_list)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (cnx) {
+        // window must be connected upon calling underlying
+        // eglCreateWindowSurface
+        if (window) {
+            window->incRef(window);
+            if (window->connect)
+                window->connect(window);
+        }
+
+        EGLSurface surface = cnx->hooks->egl.eglCreateWindowSurface(
+                dp->dpys[i], dp->configs[i][index], window, attrib_list);       
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dpy, surface, window, i, cnx);
+            return s;
+        }
+        
+        // something went wrong, disconnect and free window
+        // (will disconnect() automatically)
+        if (window) {
+            window->decRef(window);
+        }        
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
+                                    NativePixmapType pixmap,
+                                    const EGLint *attrib_list)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (cnx) {
+        EGLSurface surface = cnx->hooks->egl.eglCreatePixmapSurface(
+                dp->dpys[i], dp->configs[i][index], pixmap, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
+                                    const EGLint *attrib_list)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (cnx) {
+        EGLSurface surface = cnx->hooks->egl.eglCreatePbufferSurface(
+                dp->dpys[i], dp->configs[i][index], attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+                                    
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+
+    EGLBoolean result = s->cnx->hooks->egl.eglDestroySurface(
+            dp->dpys[s->impl], s->surface);
+    
+    delete s;
+    return result;
+}
+
+EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
+                            EGLint attribute, EGLint *value)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+
+    return s->cnx->hooks->egl.eglQuerySurface(
+            dp->dpys[s->impl], s->surface, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// contextes
+// ----------------------------------------------------------------------------
+
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
+                            EGLContext share_list, const EGLint *attrib_list)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (cnx) {
+        EGLContext context = cnx->hooks->egl.eglCreateContext(
+                dp->dpys[i], dp->configs[i][index], share_list, attrib_list);
+        if (context != EGL_NO_CONTEXT) {
+            egl_context_t* c = new egl_context_t(dpy, context, i, cnx);
+            return c;
+        }
+    }
+    return EGL_NO_CONTEXT;
+}
+
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
+{
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+    egl_display_t const * const dp = get_display(dpy);
+    egl_context_t * const c = get_context(ctx);
+    EGLBoolean result = c->cnx->hooks->egl.eglDestroyContext(
+            dp->dpys[c->impl], c->context);
+    delete c;
+    return result;
+}
+
+EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
+                            EGLSurface read, EGLContext ctx)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    if (read == EGL_NO_SURFACE && draw  == EGL_NO_SURFACE &&
+            ctx == EGL_NO_CONTEXT) 
+    {
+        EGLBoolean result = EGL_TRUE;
+        ctx = getContext();
+        if (ctx) {
+            egl_context_t * const c = get_context(ctx);
+            result = c->cnx->hooks->egl.eglMakeCurrent(dp->dpys[c->impl], 0, 0, 0);
+            if (result == EGL_TRUE) {
+                setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]);
+                setContext(EGL_NO_CONTEXT);
+            }
+        }
+        return result;
+    }
+
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;    
+    
+    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;
+    }
+    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;
+    }
+    EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
+            dp->dpys[c->impl], draw, read, c->context);
+
+    if (result == EGL_TRUE) {
+        setGlThreadSpecific(c->cnx->hooks);
+        setContext(ctx);
+        c->read = read;
+        c->draw = draw;
+    }
+    return result;
+}
+
+
+EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
+                            EGLint attribute, EGLint *value)
+{
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;    
+    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_context_t * const c = get_context(ctx);
+
+    return c->cnx->hooks->egl.eglQueryContext(
+            dp->dpys[c->impl], c->context, attribute, value);
+}
+
+EGLContext eglGetCurrentContext(void)
+{
+    EGLContext ctx = getContext();
+    return ctx;
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw)
+{
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        switch (readdraw) {
+            case EGL_READ: return c->read;
+            case EGL_DRAW: return c->draw;            
+            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLDisplay eglGetCurrentDisplay(void)
+{
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        return c->dpy;
+    }
+    return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglWaitGL(void)
+{
+    EGLBoolean res = EGL_TRUE;
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        if (uint32_t(c->impl)>=2)
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        egl_connection_t* const cnx = &gEGLImpl[c->impl];
+        if (!cnx->dso) 
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        res = cnx->hooks->egl.eglWaitGL();
+    }
+    return res;
+}
+
+EGLBoolean eglWaitNative(EGLint engine)
+{
+    EGLBoolean res = EGL_TRUE;
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        if (uint32_t(c->impl)>=2)
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        egl_connection_t* const cnx = &gEGLImpl[c->impl];
+        if (!cnx->dso) 
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        res = cnx->hooks->egl.eglWaitNative(engine);
+    }
+    return res;
+}
+
+EGLint eglGetError(void)
+{
+    EGLint result = EGL_SUCCESS;
+    for (int i=0 ; i<2 ; i++) {
+        EGLint err = EGL_SUCCESS;
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso)
+            err = cnx->hooks->egl.eglGetError();
+        if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
+            result = err;
+    }
+    if (result == EGL_SUCCESS)
+        result = getError();
+    return result;
+}
+
+void (*eglGetProcAddress(const char *procname))()
+{
+    void (*addr)();
+    addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap));
+    if (addr) return addr;
+
+    return NULL; // TODO: finish implementation below
+
+    addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap));
+    if (addr) return addr;
+    
+    addr = 0;
+    int slot = -1;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            if (cnx->hooks->egl.eglGetProcAddress) {
+                addr = cnx->hooks->egl.eglGetProcAddress(procname);
+                if (addr) {
+                    if (slot == -1) {
+                        slot = 0; // XXX: find free slot
+                        if (slot == -1) {
+                            addr = 0;
+                            break;
+                        }
+                    }
+                    cnx->hooks->ext.extensions[slot] = addr;
+                }
+            }
+        }
+    }
+    
+    if (slot >= 0) {
+        addr = 0; // XXX: address of stub 'slot'
+        gGLExtentionMap[slot].name = strdup(procname);
+        gGLExtentionMap[slot].address = addr;
+    }
+    
+    return addr;
+
+    
+    /*
+     *  TODO: For OpenGL ES extensions, we must generate a stub
+     *  that looks like
+     *      mov     r12, #0xFFFF0FFF
+     *      ldr     r12, [r12, #-15]
+     *      ldr     r12, [r12, #TLS_SLOT_OPENGL_API*4]
+     *      mov     r12, [r12, #api_offset]
+     *      ldrne   pc, r12
+     *      mov     pc, #unsupported_extension
+     * 
+     *  and write the address of the extension in *all*
+     *  gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t
+     * 
+     */
+}
+
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
+{
+    if (!validate_display_surface(dpy, draw))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(draw);
+    return s->cnx->hooks->egl.eglSwapBuffers(dp->dpys[s->impl], s->surface);
+}
+
+EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
+                            NativePixmapType target)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+    return s->cnx->hooks->egl.eglCopyBuffers(
+            dp->dpys[s->impl], s->surface, target);
+}
+
+const char* eglQueryString(EGLDisplay dpy, EGLint name)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    switch (name) {
+        case EGL_VENDOR:
+            return gVendorString;
+        case EGL_VERSION:
+            return gVersionString;
+        case EGL_EXTENSIONS:
+            return dp->extensionsString;
+        case EGL_CLIENT_APIS:
+            return gClientApiString;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)0);
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttrib(
+        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->hooks->egl.eglSurfaceAttrib) {
+        return s->cnx->hooks->egl.eglSurfaceAttrib(
+                dp->dpys[s->impl], s->surface, attribute, value);
+    }
+    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImage(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->hooks->egl.eglBindTexImage) {
+        return s->cnx->hooks->egl.eglBindTexImage(
+                dp->dpys[s->impl], s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImage(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    if (!validate_display_surface(dpy, surface))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->hooks->egl.eglReleaseTexImage) {
+        return s->cnx->hooks->egl.eglReleaseTexImage(
+                dp->dpys[s->impl], s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
+{
+    egl_display_t * const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    EGLBoolean res = EGL_TRUE;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            if (cnx->hooks->egl.eglSwapInterval) {
+                if (cnx->hooks->egl.eglSwapInterval(dp->dpys[i], interval) == EGL_FALSE) {
+                    res = EGL_FALSE;
+                }
+            }
+        }
+    }
+    return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglWaitClient(void)
+{
+    EGLBoolean res = EGL_TRUE;
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        if (uint32_t(c->impl)>=2)
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        egl_connection_t* const cnx = &gEGLImpl[c->impl];
+        if (!cnx->dso) 
+            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        if (cnx->hooks->egl.eglWaitClient) {
+            res = cnx->hooks->egl.eglWaitClient();
+        } else {
+            res = cnx->hooks->egl.eglWaitGL();
+        }
+    }
+    return res;
+}
+
+EGLBoolean eglBindAPI(EGLenum api)
+{
+    // bind this API on all EGLs
+    EGLBoolean res = EGL_TRUE;
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            if (cnx->hooks->egl.eglBindAPI) {
+                if (cnx->hooks->egl.eglBindAPI(api) == EGL_FALSE) {
+                    res = EGL_FALSE;
+                }
+            }
+        }
+    }
+    return res;
+}
+
+EGLenum eglQueryAPI(void)
+{
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            if (cnx->hooks->egl.eglQueryAPI) {
+                // the first one we find is okay, because they all
+                // should be the same
+                return cnx->hooks->egl.eglQueryAPI();
+            }
+        }
+    }
+    // or, it can only be OpenGL ES
+    return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglReleaseThread(void)
+{
+    for (int i=0 ; i<2 ; i++) {
+        egl_connection_t* const cnx = &gEGLImpl[i];
+        if (cnx->dso) {
+            if (cnx->hooks->egl.eglReleaseThread) {
+                cnx->hooks->egl.eglReleaseThread();
+            }
+        }
+    }
+    clearTLS();    
+    return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBuffer(
+          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+          EGLConfig config, const EGLint *attrib_list)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (!cnx) return EGL_FALSE;
+    if (cnx->hooks->egl.eglCreatePbufferFromClientBuffer) {
+        return cnx->hooks->egl.eglCreatePbufferFromClientBuffer(
+                dp->dpys[i], buftype, buffer, dp->configs[i][index], attrib_list);
+    }
+    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// Android extentions
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSwapRectangleANDROID(
+        EGLDisplay dpy, EGLSurface draw,
+        EGLint l, EGLint t, EGLint w, EGLint h)
+{    
+    if (!validate_display_surface(dpy, draw))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(draw);
+    if (s->cnx->hooks->egl.eglSwapRectangleANDROID) {
+        return s->cnx->hooks->egl.eglSwapRectangleANDROID(
+                dp->dpys[s->impl], s->surface, l, t, w, h);
+    }
+    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+}
+
+const char* eglQueryStringConfigANDROID(
+        EGLDisplay dpy, EGLConfig config, EGLint name)
+{
+    egl_display_t const* dp = 0;
+    int i=0, index=0;
+    egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+    if (cnx) {
+        return dp->queryString[i].extensions_config;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)0);
+}
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
new file mode 100644
index 0000000..a3dff76
--- /dev/null
+++ b/opengl/libagl/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Build the software OpenGL ES library
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	egl.cpp                     \
+	state.cpp		            \
+	texture.cpp		            \
+    Tokenizer.cpp               \
+    TokenManager.cpp            \
+    TextureObjectManager.cpp    \
+    BufferObjectManager.cpp     \
+	array.cpp.arm		        \
+	fp.cpp.arm		            \
+	light.cpp.arm		        \
+	matrix.cpp.arm		        \
+	mipmap.cpp.arm		        \
+	primitives.cpp.arm	        \
+	vertex.cpp.arm
+
+ifeq ($(TARGET_ARCH),arm)
+	LOCAL_SRC_FILES += fixed_asm.S iterators.S
+	LOCAL_CFLAGS += -fstrict-aliasing
+endif
+
+LOCAL_SHARED_LIBRARIES := libcutils libutils libpixelflinger
+LOCAL_LDLIBS := -lpthread -ldl
+LOCAL_MODULE:= libagl
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp
new file mode 100644
index 0000000..6bf28ee
--- /dev/null
+++ b/opengl/libagl/BufferObjectManager.cpp
@@ -0,0 +1,103 @@
+/*
+ ** Copyright 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.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <utils/Atomic.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Errors.h>
+
+#include <GLES/gl.h>
+
+#include "BufferObjectManager.h"
+
+
+namespace android {
+
+using namespace gl;
+
+// ----------------------------------------------------------------------------
+
+EGLBufferObjectManager::EGLBufferObjectManager() 
+: TokenManager(), mCount(0)
+{
+}
+
+EGLBufferObjectManager::~EGLBufferObjectManager()
+{
+    // destroy all the buffer objects and their storage
+    GLsizei n = mBuffers.size();
+    for (GLsizei i=0 ; i<n ; i++) {
+        buffer_t* bo = mBuffers.valueAt(i);
+        free(bo->data);
+        delete bo;
+    }
+}
+
+buffer_t const* EGLBufferObjectManager::bind(GLuint buffer)
+{
+    Mutex::Autolock _l(mLock);
+    int32_t i = mBuffers.indexOfKey(buffer);
+    if (i >= 0) {
+        return mBuffers.valueAt(i);
+    }
+    buffer_t* bo = new buffer_t;
+    bo->data = 0;
+    bo->usage = GL_STATIC_DRAW;
+    bo->size = 0;
+    bo->name = buffer;
+    mBuffers.add(buffer, bo);
+    return bo;
+}
+
+int EGLBufferObjectManager::allocateStore(buffer_t* bo,
+        GLsizeiptr size, GLenum usage)
+{
+    Mutex::Autolock _l(mLock);
+    if (size != bo->size) {
+       uint8_t* data = (uint8_t*)malloc(size);
+        if (data == 0)
+            return -1;
+        free(bo->data);
+        bo->data = data;
+        bo->size = size;
+    }
+    bo->usage = usage;
+    return 0;
+}
+
+void EGLBufferObjectManager::deleteBuffers(GLsizei n, const GLuint* buffers)
+{
+    Mutex::Autolock _l(mLock);
+    while (n--) {
+        const GLuint t = *buffers++;
+        if (t) {
+            int32_t index = mBuffers.indexOfKey(t);
+            if (index >= 0) {
+                buffer_t* bo = mBuffers.valueAt(index);
+                free(bo->data);
+                mBuffers.removeItemsAt(index);
+                delete bo;
+            }
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/opengl/libagl/BufferObjectManager.h b/opengl/libagl/BufferObjectManager.h
new file mode 100644
index 0000000..9e9340a
--- /dev/null
+++ b/opengl/libagl/BufferObjectManager.h
@@ -0,0 +1,85 @@
+/*
+ **
+ ** Copyright 2006, 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.
+ */
+
+#ifndef ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H
+#define ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <utils/Atomic.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Errors.h>
+
+#include <GLES/gl.h>
+
+#include "Tokenizer.h"
+#include "TokenManager.h"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+namespace gl {
+
+struct buffer_t {
+    GLsizeiptr      size;
+    GLenum          usage;
+    uint8_t*        data;
+    uint32_t        name;
+};
+
+};
+
+class EGLBufferObjectManager : public TokenManager
+{
+public:
+    EGLBufferObjectManager();
+    ~EGLBufferObjectManager();
+
+    // protocol for sp<>
+    inline  void    incStrong(const void* id) const;
+    inline  void    decStrong(const void* id) const;
+    typedef void    weakref_type;
+
+    gl::buffer_t const* bind(GLuint buffer);
+    int                 allocateStore(gl::buffer_t* bo, GLsizeiptr size, GLenum usage);
+    void                deleteBuffers(GLsizei n, const GLuint* buffers);
+
+private:
+    mutable volatile int32_t            mCount;
+    mutable Mutex                       mLock;
+    KeyedVector<GLuint, gl::buffer_t*>  mBuffers;
+};
+
+void EGLBufferObjectManager::incStrong(const void* id) const {
+    android_atomic_inc(&mCount);
+}
+void EGLBufferObjectManager::decStrong(const void* id) const {
+    if (android_atomic_dec(&mCount) == 1) {
+        delete this;
+    }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H
+
diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp
new file mode 100644
index 0000000..12fae63
--- /dev/null
+++ b/opengl/libagl/TextureObjectManager.cpp
@@ -0,0 +1,309 @@
+/*
+ ** Copyright 2006, 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "context.h"
+#include "TextureObjectManager.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+EGLTextureObject::EGLTextureObject()
+    : mCount(0), mSize(0)
+{
+    init();
+}
+
+EGLTextureObject::~EGLTextureObject()
+{
+    if (!direct) {
+        if (mSize && surface.data)
+            free(surface.data);
+        if (mMipmaps)
+            freeMipmaps();
+    }
+}
+
+void EGLTextureObject::init()
+{
+    memset(&surface, 0, sizeof(surface));
+    surface.version = sizeof(surface);
+    mMipmaps = 0;
+    mNumExtraLod = 0;
+    mIsComplete = false;
+    wraps = GL_REPEAT;
+    wrapt = GL_REPEAT;
+    min_filter = GL_LINEAR;
+    mag_filter = GL_LINEAR;
+    internalformat = 0;
+    memset(crop_rect, 0, sizeof(crop_rect));
+    generate_mipmap = GL_FALSE;
+    direct = GL_FALSE;
+}
+
+void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old)
+{
+    wraps = old->wraps;
+    wrapt = old->wrapt;
+    min_filter = old->min_filter;
+    mag_filter = old->mag_filter;
+    memcpy(crop_rect, old->crop_rect, sizeof(crop_rect));
+    generate_mipmap = old->generate_mipmap;
+    direct = old->direct;
+}
+
+status_t EGLTextureObject::allocateMipmaps()
+{
+    // here, by construction, mMipmaps=0 && mNumExtraLod=0
+
+    if (!surface.data)
+        return NO_INIT;
+
+    int w = surface.width;
+    int h = surface.height;
+    const int numLods = 31 - gglClz(max(w,h));
+    if (numLods <= 0)
+        return NO_ERROR;
+
+    mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface));
+    if (!mMipmaps)
+        return NO_MEMORY;
+
+    memset(mMipmaps, 0, numLods * sizeof(GGLSurface));
+    mNumExtraLod = numLods;
+    return NO_ERROR;
+}
+
+void EGLTextureObject::freeMipmaps()
+{
+    if (mMipmaps) {
+        for (int i=0 ; i<mNumExtraLod ; i++) {
+            if (mMipmaps[i].data) {
+                free(mMipmaps[i].data);
+            }
+        }
+        free(mMipmaps);
+        mMipmaps = 0;
+        mNumExtraLod = 0;
+    }
+}
+
+const GGLSurface& EGLTextureObject::mip(int lod) const
+{
+    if (lod<=0 || !mMipmaps)
+        return surface;
+    lod = min(lod-1, mNumExtraLod-1);
+    return mMipmaps[lod];
+}
+
+GGLSurface& EGLTextureObject::editMip(int lod)
+{
+    return const_cast<GGLSurface&>(mip(lod));
+}
+
+status_t EGLTextureObject::setSurface(GGLSurface const* s)
+{
+    // XXX: glFlush() on 's'
+    if (mSize && surface.data) {
+        free(surface.data);
+    }
+    surface = *s;
+    internalformat = 0;
+
+    // we should keep the crop_rect, but it's delicate because
+    // the new size of the surface could make it invalid.
+    // so for now, we just loose it.
+    memset(crop_rect, 0, sizeof(crop_rect));
+
+    // it would be nice id we could keep the generate_mipmap flag
+    // we would have to generate them right now though.
+    generate_mipmap = GL_FALSE;
+
+    direct = GL_TRUE;
+    mSize = 0;  // we don't own this surface
+    if (mMipmaps)
+        freeMipmaps();
+    mIsComplete = true;
+    return NO_ERROR;
+}
+
+status_t EGLTextureObject::reallocate(
+        GLint level, int w, int h, int s,
+        int format, int compressedFormat, int bpr)
+{
+    const size_t size = h * bpr;
+    if (level == 0) 
+    {
+        if (size!=mSize || !surface.data) {
+            if (mSize && surface.data) {
+                free(surface.data);
+            }
+            surface.data = (GGLubyte*)malloc(size);
+            if (!surface.data) {
+                mSize = 0;
+                mIsComplete = false;
+                return NO_MEMORY;
+            }
+            mSize = size;
+        }
+        surface.version = sizeof(GGLSurface);
+        surface.width  = w;
+        surface.height = h;
+        surface.stride = s;
+        surface.format = format;
+        surface.compressedFormat = compressedFormat;
+        if (mMipmaps)
+            freeMipmaps();
+        mIsComplete = true;
+    }
+    else
+    {
+        if (!mMipmaps) {
+            if (allocateMipmaps() != NO_ERROR)
+                return NO_MEMORY;
+        }
+
+        LOGW_IF(level-1 >= mNumExtraLod, 
+                "specifying mipmap level %d, but # of level is %d",
+                level, mNumExtraLod+1);        
+
+        GGLSurface& mipmap = editMip(level);
+        if (mipmap.data)
+            free(mipmap.data);
+
+        mipmap.data = (GGLubyte*)malloc(size);
+        if (!mipmap.data) {
+            memset(&mipmap, 0, sizeof(GGLSurface));
+            mIsComplete = false;
+            return NO_MEMORY;
+        }
+
+        mipmap.version = sizeof(GGLSurface);
+        mipmap.width  = w;
+        mipmap.height = h;
+        mipmap.stride = s;
+        mipmap.format = format;
+        mipmap.compressedFormat = compressedFormat;
+
+        // check if the texture is complete
+        mIsComplete = true;
+        const GGLSurface* prev = &surface;
+        for (int i=0 ; i<mNumExtraLod ; i++) {
+            const GGLSurface* curr = mMipmaps + i;
+            if (curr->format != surface.format) {
+                mIsComplete = false;
+                break;
+            }
+
+            uint32_t w = (prev->width  >> 1) ? : 1;
+            uint32_t h = (prev->height >> 1) ? : 1;
+            if (w != curr->width || h != curr->height) {
+                mIsComplete = false;
+                break;
+            }
+            prev = curr;
+        }
+    }
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+EGLSurfaceManager::EGLSurfaceManager()
+    : TokenManager(), mCount(0)
+{
+}
+
+EGLSurfaceManager::~EGLSurfaceManager()
+{
+    // everything gets freed automatically here...
+}
+
+sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name)
+{
+    sp<EGLTextureObject> result;
+
+    Mutex::Autolock _l(mLock);
+    if (mTextures.indexOfKey(name) >= 0)
+        return result; // already exists!
+
+    result = new EGLTextureObject();
+
+    status_t err = mTextures.add(name, result);
+    if (err < 0)
+        result.clear();
+
+    return result;
+}
+
+sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name)
+{
+    Mutex::Autolock _l(mLock);
+    const ssize_t index = mTextures.indexOfKey(name);
+    if (index >= 0) {
+        sp<EGLTextureObject> result(mTextures.valueAt(index));
+        mTextures.removeItemsAt(index);
+        return result;
+    }
+    return 0;
+}
+
+sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name)
+{
+    sp<EGLTextureObject> tex;
+    Mutex::Autolock _l(mLock);
+    const ssize_t index = mTextures.indexOfKey(name);
+    if (index >= 0) {
+        const sp<EGLTextureObject>& old = mTextures.valueAt(index);
+        const uint32_t refs = old->getStrongCount();
+        if (ggl_likely(refs == 1)) {
+            // we're the only owner
+            tex = old;
+        } else {
+            // keep the texture's parameters
+            tex = new EGLTextureObject();
+            tex->copyParameters(old);
+            mTextures.removeItemsAt(index);
+            mTextures.add(name, tex);
+        }
+    }
+    return tex;
+}
+
+void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens)
+{
+    // free all texures
+    Mutex::Autolock _l(mLock);
+    for (GLsizei i=0 ; i<n ; i++) {
+        const GLuint t(*tokens++);
+        if (t) {
+            mTextures.removeItem(t);
+        }
+    }
+}
+
+sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name)
+{
+    Mutex::Autolock _l(mLock);
+    const ssize_t index = mTextures.indexOfKey(name);
+    if (index >= 0)
+        return mTextures.valueAt(index);
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h
new file mode 100644
index 0000000..74ed1a4
--- /dev/null
+++ b/opengl/libagl/TextureObjectManager.h
@@ -0,0 +1,140 @@
+/*
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_SURFACE_H
+#define ANDROID_OPENGLES_SURFACE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <utils/Atomic.h>
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Errors.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include <GLES/gl.h>
+
+#include "Tokenizer.h"
+#include "TokenManager.h"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class EGLTextureObject
+{
+public:
+                    EGLTextureObject();
+                   ~EGLTextureObject();
+
+    // protocol for sp<>
+    inline  void        incStrong(const void* id) const;
+    inline  void        decStrong(const void* id) const;
+    inline  uint32_t    getStrongCount() const;
+
+    status_t            setSurface(GGLSurface const* s);
+    status_t            reallocate(GLint level,
+                            int w, int h, int s,
+                            int format, int compressedFormat, int bpr);
+    inline  size_t      size() const;
+    const GGLSurface&   mip(int lod) const;
+    GGLSurface&         editMip(int lod);
+    bool                hasMipmaps() const { return mMipmaps!=0; }
+    bool                isComplete() const { return mIsComplete; }
+    void                copyParameters(const sp<EGLTextureObject>& old);
+
+private:
+        status_t        allocateMipmaps();
+            void        freeMipmaps();
+            void        init();
+    mutable int32_t     mCount;
+    size_t              mSize;
+    GGLSurface          *mMipmaps;
+    int                 mNumExtraLod;
+    bool                mIsComplete;
+
+public:
+    GGLSurface          surface;
+    GLenum              wraps;
+    GLenum              wrapt;
+    GLenum              min_filter;
+    GLenum              mag_filter;
+    GLenum              internalformat;
+    GLint               crop_rect[4];
+    GLint               generate_mipmap;
+    GLint               direct;
+};
+
+void EGLTextureObject::incStrong(const void* id) const {
+    android_atomic_inc(&mCount);
+}
+void EGLTextureObject::decStrong(const void* id) const {
+    if (android_atomic_dec(&mCount) == 1) {
+        delete this;
+    }
+}
+uint32_t EGLTextureObject::getStrongCount() const {
+    return mCount;
+}
+size_t EGLTextureObject::size() const {
+    return mSize;
+}
+
+// ----------------------------------------------------------------------------
+
+class EGLSurfaceManager : public TokenManager
+{
+public:
+                EGLSurfaceManager();
+                ~EGLSurfaceManager();
+
+    // protocol for sp<>
+    inline  void    incStrong(const void* id) const;
+    inline  void    decStrong(const void* id) const;
+    typedef void    weakref_type;
+
+    sp<EGLTextureObject>    createTexture(GLuint name);
+    sp<EGLTextureObject>    removeTexture(GLuint name);
+    sp<EGLTextureObject>    replaceTexture(GLuint name);
+    void                    deleteTextures(GLsizei n, const GLuint *tokens);
+    sp<EGLTextureObject>    texture(GLuint name);
+
+private:
+    mutable int32_t                             mCount;
+    mutable Mutex                               mLock;
+    KeyedVector< GLuint, sp<EGLTextureObject> > mTextures;
+};
+
+void EGLSurfaceManager::incStrong(const void* id) const {
+    android_atomic_inc(&mCount);
+}
+void EGLSurfaceManager::decStrong(const void* id) const {
+    if (android_atomic_dec(&mCount) == 1) {
+        delete this;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_SURFACE_H
+
diff --git a/opengl/libagl/TokenManager.cpp b/opengl/libagl/TokenManager.cpp
new file mode 100644
index 0000000..eea6025
--- /dev/null
+++ b/opengl/libagl/TokenManager.cpp
@@ -0,0 +1,62 @@
+/* libs/opengles/surface.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "TokenManager.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+TokenManager::TokenManager()
+{
+    // token 0 is always reserved
+    mTokenizer.reserve(0);
+}
+
+TokenManager::~TokenManager()
+{
+}
+
+status_t TokenManager::getToken(GLsizei n, GLuint *tokens)
+{
+    Mutex::Autolock _l(mLock);
+    for (GLsizei i=0 ; i<n ; i++)
+        *tokens++ = mTokenizer.acquire();
+    return NO_ERROR;
+}
+
+void TokenManager::recycleTokens(GLsizei n, const GLuint *tokens)
+{
+    Mutex::Autolock _l(mLock);
+    for (int i=0 ; i<n ; i++) {
+        const GLuint token = *tokens++;
+        if (token) {
+            mTokenizer.release(token);
+        }
+    }
+}
+
+bool TokenManager::isTokenValid(GLuint token) const
+{
+    Mutex::Autolock _l(mLock);
+    return mTokenizer.isAcquired(token);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/opengl/libagl/TokenManager.h b/opengl/libagl/TokenManager.h
new file mode 100644
index 0000000..49c1469
--- /dev/null
+++ b/opengl/libagl/TokenManager.h
@@ -0,0 +1,53 @@
+/*
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_TOKEN_MANAGER_H
+#define ANDROID_OPENGLES_TOKEN_MANAGER_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+
+#include <GLES/gl.h>
+
+#include "Tokenizer.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class TokenManager
+{
+public:
+                TokenManager();
+                ~TokenManager();
+
+    status_t    getToken(GLsizei n, GLuint *tokens);
+    void        recycleTokens(GLsizei n, const GLuint *tokens);
+    bool        isTokenValid(GLuint token) const;
+
+private:
+    mutable Mutex   mLock;
+    Tokenizer       mTokenizer;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_TOKEN_MANAGER_H
+
diff --git a/opengl/libagl/Tokenizer.cpp b/opengl/libagl/Tokenizer.cpp
new file mode 100644
index 0000000..9b3ea1a
--- /dev/null
+++ b/opengl/libagl/Tokenizer.cpp
@@ -0,0 +1,173 @@
+/* libs/opengles/Tokenizer.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+
+#include "Tokenizer.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t)
+
+Tokenizer::Tokenizer()
+{
+}
+
+Tokenizer::Tokenizer(const Tokenizer& other)
+    : mRanges(other.mRanges)
+{
+}
+
+Tokenizer::~Tokenizer()
+{
+}
+
+uint32_t Tokenizer::acquire()
+{
+    if (!mRanges.size() || mRanges[0].first) {
+        _insertTokenAt(0,0);
+        return 0;
+    }
+    
+    // just extend the first run
+    const run_t& run = mRanges[0];
+    uint32_t token = run.first + run.length;
+    _insertTokenAt(token, 1);
+    return token;
+}
+
+bool Tokenizer::isAcquired(uint32_t token) const
+{
+    return (_indexOrderOf(token) >= 0);
+}
+
+status_t Tokenizer::reserve(uint32_t token)
+{
+    size_t o;
+    const ssize_t i = _indexOrderOf(token, &o);
+    if (i >= 0) {
+        return BAD_VALUE; // this token is already taken
+    }
+    ssize_t err = _insertTokenAt(token, o);
+    return (err<0) ? err : status_t(NO_ERROR);
+}
+
+status_t Tokenizer::release(uint32_t token)
+{
+    const ssize_t i = _indexOrderOf(token);
+    if (i >= 0) {
+        const run_t& run = mRanges[i];
+        if ((token >= run.first) && (token < run.first+run.length)) {
+            // token in this range, we need to split
+            run_t& run = mRanges.editItemAt(i);
+            if ((token == run.first) || (token == run.first+run.length-1)) {
+                if (token == run.first) {
+                    run.first += 1;
+                }
+                run.length -= 1;
+                if (run.length == 0) {
+                    // XXX: should we systematically remove a run that's empty?
+                    mRanges.removeItemsAt(i);
+                }
+            } else {
+                // split the run
+                run_t new_run;
+                new_run.first = token+1;
+                new_run.length = run.first+run.length - new_run.first;
+                run.length = token - run.first;
+                mRanges.insertAt(new_run, i+1);
+            }
+            return NO_ERROR;
+        }
+    }
+    return NAME_NOT_FOUND;
+}
+
+ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const
+{
+    // binary search
+    ssize_t err = NAME_NOT_FOUND;
+    ssize_t l = 0;
+    ssize_t h = mRanges.size()-1;
+    ssize_t mid;
+    const run_t* a = mRanges.array();
+    while (l <= h) {
+        mid = l + (h - l)/2;
+        const run_t* const curr = a + mid;
+        int c = 0;
+        if (token < curr->first)                        c = 1;
+        else if (token >= curr->first+curr->length)     c = -1;
+        if (c == 0) {
+            err = l = mid;
+            break;
+        } else if (c < 0) {
+            l = mid + 1;
+        } else {
+            h = mid - 1;
+        }
+    }
+    if (order) *order = l;
+    return err;
+}
+
+ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index)
+{
+    const size_t c = mRanges.size();
+
+    if (index >= 1) {
+        // do we need to merge with the previous run?
+        run_t& p = mRanges.editItemAt(index-1);
+        if (p.first+p.length == token) {
+            p.length += 1;
+            if (index < c) {
+                const run_t& n = mRanges[index];
+                if (token+1 == n.first) {
+                    p.length += n.length;
+                    mRanges.removeItemsAt(index);
+                }
+            }
+            return index;
+        }
+    }
+    
+    if (index < c) {
+        // do we need to merge with the next run?
+        run_t& n = mRanges.editItemAt(index);
+        if (token+1 == n.first) {
+            n.first -= 1;
+            n.length += 1;
+            return index;
+        }
+    }
+
+    return mRanges.insertAt(run_t(token,1), index);
+}
+
+void Tokenizer::dump() const
+{
+    const run_t* ranges = mRanges.array();
+    const size_t c = mRanges.size();
+    LOGD("Tokenizer (%p, size = %u)\n", this, c);
+    for (size_t i=0 ; i<c ; i++) {
+        LOGD("%u: (%u, %u)\n", i, ranges[i].first, ranges[i].length);
+    }
+}
+
+}; // namespace android
+
diff --git a/opengl/libagl/Tokenizer.h b/opengl/libagl/Tokenizer.h
new file mode 100644
index 0000000..ac555cb
--- /dev/null
+++ b/opengl/libagl/Tokenizer.h
@@ -0,0 +1,59 @@
+/* libs/opengles/Tokenizer.h
+**
+** Copyright 2006, 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.
+*/
+
+
+#ifndef ANDROID_OPENGLES_TOKENIZER_H
+#define ANDROID_OPENGLES_TOKENIZER_H
+
+#include <utils/Vector.h>
+#include <utils/Errors.h>
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+class Tokenizer
+{
+public:
+                Tokenizer();
+                Tokenizer(const Tokenizer& other);
+                ~Tokenizer();
+
+    uint32_t    acquire();
+    status_t    reserve(uint32_t token);
+    status_t    release(uint32_t token);
+    bool        isAcquired(uint32_t token) const;
+
+    void dump() const;
+
+    struct run_t {
+        run_t() {};
+        run_t(uint32_t f, uint32_t l) : first(f), length(l) {}
+        uint32_t    first;
+        uint32_t    length;
+    };
+private:
+    ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const;
+    ssize_t _insertTokenAt(uint32_t token, size_t index);
+    Vector<run_t>   mRanges;
+};
+
+}; // namespace android
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_OPENGLES_TOKENIZER_H
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
new file mode 100644
index 0000000..1f6757d
--- /dev/null
+++ b/opengl/libagl/array.cpp
@@ -0,0 +1,1557 @@
+/* 
+** Copyright 2006, 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "light.h"
+#include "primitives.h"
+#include "texture.h"
+#include "BufferObjectManager.h"
+
+// ----------------------------------------------------------------------------
+
+#define VC_CACHE_STATISTICS     0
+#define VC_CACHE_TYPE_NONE      0
+#define VC_CACHE_TYPE_INDEXED   1
+#define VC_CACHE_TYPE_LRU       2
+#define VC_CACHE_TYPE           VC_CACHE_TYPE_INDEXED
+
+#if VC_CACHE_STATISTICS
+#include <utils/Timers.h>
+#endif
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static void validate_arrays(ogles_context_t* c, GLenum mode);
+
+static void compileElements__generic(ogles_context_t*,
+        vertex_t*, GLint, GLsizei);
+static void compileElement__generic(ogles_context_t*,
+        vertex_t*, GLint);
+
+static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei);
+static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei);
+
+static void drawIndexedPrimitivesPoints(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesLineStrip(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesLineLoop(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesLines(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesTriangleFan(ogles_context_t*,
+        GLsizei, const GLvoid*);
+static void drawIndexedPrimitivesTriangles(ogles_context_t*,
+        GLsizei, const GLvoid*);
+
+// ----------------------------------------------------------------------------
+
+typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei);
+static const arrays_prims_fct_t drawArraysPrims[] = {
+    drawPrimitivesPoints,
+    drawPrimitivesLines,
+    drawPrimitivesLineLoop,
+    drawPrimitivesLineStrip,
+    drawPrimitivesTriangles,
+    drawPrimitivesTriangleStrip,
+    drawPrimitivesTriangleFan
+};
+
+typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*);
+static const elements_prims_fct_t drawElementsPrims[] = {
+    drawIndexedPrimitivesPoints,
+    drawIndexedPrimitivesLines,
+    drawIndexedPrimitivesLineLoop,
+    drawIndexedPrimitivesLineStrip,
+    drawIndexedPrimitivesTriangles,
+    drawIndexedPrimitivesTriangleStrip,
+    drawIndexedPrimitivesTriangleFan
+};
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void ogles_init_array(ogles_context_t* c)
+{
+    c->arrays.vertex.size = 4;
+    c->arrays.vertex.type = GL_FLOAT;
+    c->arrays.color.size = 4;
+    c->arrays.color.type = GL_FLOAT;
+    c->arrays.normal.size = 4;
+    c->arrays.normal.type = GL_FLOAT;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        c->arrays.texture[i].size = 4;
+        c->arrays.texture[i].type = GL_FLOAT;
+    }
+    c->vc.init();
+
+    if (!c->vc.vBuffer) {
+        // this could have failed
+        ogles_error(c, GL_OUT_OF_MEMORY);
+    }
+}
+
+void ogles_uninit_array(ogles_context_t* c)
+{
+    c->vc.uninit();
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Array fetchers
+#endif
+
+static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) {
+    memcpy(v, c->current.color.v, sizeof(vec4_t));
+}
+static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) {
+    memcpy(v, c->currentColorClamped.v, sizeof(vec4_t));
+}
+static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) {
+    memcpy(v, c->currentNormal.v, sizeof(vec3_t));
+}
+static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) {
+    memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t));
+}
+
+
+static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) {
+}
+static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+}
+static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+}
+static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
+    memcpy(v, p, 2*sizeof(GLfixed));
+}
+static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
+    v[0] = gglFloatToFixed(p[0]);
+    v[1] = gglFloatToFixed(p[1]);
+}
+static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+    v[2] = gglIntToFixed(p[2]);
+}
+static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+    v[2] = gglIntToFixed(p[2]);
+}
+static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
+    memcpy(v, p, 3*sizeof(GLfixed));
+}
+static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
+    v[0] = gglFloatToFixed(p[0]);
+    v[1] = gglFloatToFixed(p[1]);
+    v[2] = gglFloatToFixed(p[2]);
+}
+static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+    v[2] = gglIntToFixed(p[2]);
+    v[3] = gglIntToFixed(p[3]);
+}
+static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) {
+    v[0] = gglIntToFixed(p[0]);
+    v[1] = gglIntToFixed(p[1]);
+    v[2] = gglIntToFixed(p[2]);
+    v[3] = gglIntToFixed(p[3]);
+}
+static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
+    memcpy(v, p, 4*sizeof(GLfixed));
+}
+static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
+    v[0] = gglFloatToFixed(p[0]);
+    v[1] = gglFloatToFixed(p[1]);
+    v[2] = gglFloatToFixed(p[2]);
+    v[3] = gglFloatToFixed(p[3]);
+}
+static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
+    v[0] = GGL_UB_TO_X(p[0]);
+    v[1] = GGL_UB_TO_X(p[1]);
+    v[2] = GGL_UB_TO_X(p[2]);
+    v[3] = GGL_UB_TO_X(p[3]);
+}
+static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
+    v[0] = gglClampx(p[0]);
+    v[1] = gglClampx(p[1]);
+    v[2] = gglClampx(p[2]);
+    v[3] = gglClampx(p[3]);
+}
+static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
+    v[0] = gglClampx(gglFloatToFixed(p[0]));
+    v[1] = gglClampx(gglFloatToFixed(p[1]));
+    v[2] = gglClampx(gglFloatToFixed(p[2]));
+    v[3] = gglClampx(gglFloatToFixed(p[3]));
+}
+static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
+    v[0] = GGL_UB_TO_X(p[0]);
+    v[1] = GGL_UB_TO_X(p[1]);
+    v[2] = GGL_UB_TO_X(p[2]);
+    v[3] = 0x10000;
+}
+static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
+    v[0] = gglClampx(p[0]);
+    v[1] = gglClampx(p[1]);
+    v[2] = gglClampx(p[2]);
+    v[3] = 0x10000;
+}
+static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
+    v[0] = gglClampx(gglFloatToFixed(p[0]));
+    v[1] = gglClampx(gglFloatToFixed(p[1]));
+    v[2] = gglClampx(gglFloatToFixed(p[2]));
+    v[3] = 0x10000;
+}
+static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
+    v[0] = GGL_B_TO_X(p[0]);
+    v[1] = GGL_B_TO_X(p[1]);
+    v[2] = GGL_B_TO_X(p[2]);
+}
+static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
+    v[0] = GGL_S_TO_X(p[0]);
+    v[1] = GGL_S_TO_X(p[1]);
+    v[2] = GGL_S_TO_X(p[2]);
+}
+
+typedef array_t::fetcher_t fn_t; 
+
+static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x}
+    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
+         (fn_t)fetch3f, 0, 0, 0, 0, 0,
+         (fn_t)fetch3x },
+    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
+         (fn_t)fetch4f, 0, 0, 0, 0, 0,
+         (fn_t)fetch4x },
+};
+static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x}
+    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
+         (fn_t)fetchClamp3f, 0, 0, 0, 0, 0,
+         (fn_t)fetchClamp3x },
+    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
+         (fn_t)fetchClamp4f, 0, 0, 0, 0, 0,
+         (fn_t)fetchClamp4x },
+};
+static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x}
+    { (fn_t)fetchExpand3b, 0,
+      (fn_t)fetchExpand3s, 0, 0, 0,
+      (fn_t)fetch3f, 0, 0, 0, 0, 0,
+      (fn_t)fetch3x },
+};
+static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
+    { (fn_t)fetch2b, 0,
+      (fn_t)fetch2s, 0, 0, 0,
+      (fn_t)fetch2f, 0, 0, 0, 0, 0,
+      (fn_t)fetch3x },
+    { (fn_t)fetch3b, 0,
+      (fn_t)fetch3s, 0, 0, 0,
+      (fn_t)fetch3f, 0, 0, 0, 0, 0,
+      (fn_t)fetch3x },
+    { (fn_t)fetch4b, 0,
+      (fn_t)fetch4s, 0, 0, 0,
+      (fn_t)fetch4f, 0, 0, 0, 0, 0,
+      (fn_t)fetch4x }
+};
+static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
+    { (fn_t)fetch2b, 0,
+      (fn_t)fetch2s, 0, 0, 0,
+      (fn_t)fetch2f, 0, 0, 0, 0, 0,
+      (fn_t)fetch2x },
+    { (fn_t)fetch3b, 0,
+      (fn_t)fetch3s, 0, 0, 0,
+      (fn_t)fetch3f, 0, 0, 0, 0, 0,
+      (fn_t)fetch3x },
+    { (fn_t)fetch4b, 0,
+      (fn_t)fetch4s, 0, 0, 0,
+      (fn_t)fetch4f, 0, 0, 0, 0, 0,
+      (fn_t)fetch4x }
+};
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark array_t
+#endif
+
+void array_t::init(
+        GLint size, GLenum type, GLsizei stride,
+        const GLvoid *pointer, const buffer_t* bo, GLsizei count)
+{
+    if (!stride) {
+        stride = size;
+        switch (type) {
+        case GL_SHORT:
+        case GL_UNSIGNED_SHORT:
+            stride *= 2;
+            break;
+        case GL_FLOAT:
+        case GL_FIXED:
+            stride *= 4;
+            break;
+        }
+    }
+    this->size = size;
+    this->type = type;
+    this->stride = stride;
+    this->pointer = pointer;
+    this->bo = bo;
+    this->bounds = count;
+}
+
+inline void array_t::resolve() 
+{
+    physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark vertex_cache_t
+#endif
+
+void vertex_cache_t::init()
+{
+    // make sure the size of vertex_t allows cache-line alignment
+    CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize;
+
+    const int align = 32;
+    const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
+    const size_t size = s*sizeof(vertex_t) + align;
+    base = malloc(size);
+    if (base) {
+        memset(base, 0, size);
+        vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1));
+        vCache = vBuffer + VERTEX_BUFFER_SIZE;
+        sequence = 0;
+    }
+}
+
+void vertex_cache_t::uninit()
+{
+    free(base);
+    base = vBuffer = vCache = 0;
+}
+
+void vertex_cache_t::clear()
+{
+#if VC_CACHE_STATISTICS
+    startTime = systemTime(SYSTEM_TIME_THREAD);
+    total = 0;
+    misses = 0;
+#endif
+
+#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
+    vertex_t* v = vBuffer;
+    size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
+    do {
+        v->mru = 0;
+        v++;
+    } while (--count);
+#endif
+
+    sequence += INDEX_SEQ;
+    if (sequence >= 0x80000000LU) {
+        sequence = INDEX_SEQ;
+        vertex_t* v = vBuffer;
+        size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
+        do {
+            v->index = 0;
+            v++;
+        } while (--count);
+    }
+}
+
+void vertex_cache_t::dump_stats(GLenum mode)
+{
+#if VC_CACHE_STATISTICS
+    nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime;
+    uint32_t hits = total - misses;
+    uint32_t prim_count;
+    switch (mode) {
+    case GL_POINTS:             prim_count = total;         break;
+    case GL_LINE_STRIP:         prim_count = total - 1;     break;
+    case GL_LINE_LOOP:          prim_count = total - 1;     break;
+    case GL_LINES:              prim_count = total / 2;     break;
+    case GL_TRIANGLE_STRIP:     prim_count = total - 2;     break;
+    case GL_TRIANGLE_FAN:       prim_count = total - 2;     break;
+    case GL_TRIANGLES:          prim_count = total / 3;     break;
+    default:    return;
+    }
+    printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%,"
+            " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n",
+            total, hits, misses, (hits*100)/total,
+            prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time),
+            float(misses) / prim_count);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+static __attribute__((noinline))
+void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable)
+{
+    const int tmu = c->arrays.activeTexture;
+    array_t* a;
+    switch (array) {
+    case GL_COLOR_ARRAY:            a = &c->arrays.color;           break;
+    case GL_NORMAL_ARRAY:           a = &c->arrays.normal;          break;
+    case GL_TEXTURE_COORD_ARRAY:    a = &c->arrays.texture[tmu];    break;
+    case GL_VERTEX_ARRAY:           a = &c->arrays.vertex;          break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    a->enable = enable ? GL_TRUE : GL_FALSE;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Vertex Cache
+#endif
+
+static __attribute__((noinline))
+vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index)
+{
+    #if VC_CACHE_STATISTICS
+        c->vc.misses++;
+    #endif
+    if (ggl_unlikely(v->locked)) {
+        // we're just looking for an entry in the cache that is not locked.
+        // and we know that there cannot be more than 2 locked entries
+        // because a triangle needs at most 3 vertices.
+        // We never use the first and second entries because they might be in
+        // use by the striper or faner. Any other entry will do as long as
+        // it's not locked.
+        // We compute directly the index of a "free" entry from the locked
+        // state of v[2] and v[3].
+        v = c->vc.vBuffer + 2;
+        v += v[0].locked | (v[1].locked<<1);       
+    }
+    // note: compileElement clears v->flags
+    c->arrays.compileElement(c, v, index);
+    v->locked = 1;
+    return v;
+}
+
+static __attribute__((noinline))
+vertex_t* fetch_vertex(ogles_context_t* c, size_t index)
+{
+    index |= c->vc.sequence;
+
+#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED
+
+    vertex_t* const v = c->vc.vCache + 
+            (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1));
+
+    if (ggl_likely(v->index == index)) {
+        v->locked = 1;
+        return v;
+    }
+    return cache_vertex(c, v, index);
+
+#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
+
+    vertex_t* v = c->vc.vCache + 
+            (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2;
+
+    // always record LRU in v[0]
+    if (ggl_likely(v[0].index == index)) {
+        v[0].locked = 1;
+        v[0].mru = 0;
+        return &v[0];
+    }
+
+    if (ggl_likely(v[1].index == index)) {
+        v[1].locked = 1;
+        v[0].mru = 1;
+        return &v[1];
+    }
+
+    const int lru = 1 - v[0].mru;
+    v[0].mru = lru;
+    return cache_vertex(c, &v[lru], index);
+
+#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE
+
+    // just for debugging...
+    vertex_t* v = c->vc.vBuffer + 2;
+    return cache_vertex(c, v, index);
+
+#endif
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Primitive Assembly
+#endif
+
+void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (ggl_unlikely(count < 1))
+        return;
+
+    // vertex cache size must be multiple of 1
+    const GLsizei vcs = 
+            (vertex_cache_t::VERTEX_BUFFER_SIZE +
+             vertex_cache_t::VERTEX_CACHE_SIZE);
+    do {
+        vertex_t* v = c->vc.vBuffer;
+        GLsizei num = count > vcs ? vcs : count; 
+        c->arrays.cull = vertex_t::CLIP_ALL;
+        c->arrays.compileElements(c, v, first, num);
+        first += num;
+        count -= num;
+        if (!c->arrays.cull) {
+            // quick/trivial reject of the whole batch
+            do {
+                const uint32_t cc = v[0].flags;
+                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                    c->prims.renderPoint(c, v);
+                v++;
+                num--;
+            } while (num);
+        }
+    } while (count);
+}
+
+// ----------------------------------------------------------------------------
+
+void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (ggl_unlikely(count < 2))
+        return;
+
+    vertex_t *v, *v0, *v1;
+    c->arrays.cull = vertex_t::CLIP_ALL;
+    c->arrays.compileElement(c, c->vc.vBuffer, first);
+    first += 1;
+    count -= 1;
+
+    // vertex cache size must be multiple of 1
+    const GLsizei vcs = 
+        (vertex_cache_t::VERTEX_BUFFER_SIZE +
+         vertex_cache_t::VERTEX_CACHE_SIZE - 1);
+    do {
+        v0 = c->vc.vBuffer + 0; 
+        v  = c->vc.vBuffer + 1;
+        GLsizei num = count > vcs ? vcs : count; 
+        c->arrays.compileElements(c, v, first, num);
+        first += num;
+        count -= num;
+        if (!c->arrays.cull) {
+            // quick/trivial reject of the whole batch
+            do {
+                v1 = v++;
+                const uint32_t cc = v0->flags & v1->flags;
+                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                    c->prims.renderLine(c, v0, v1);
+                v0 = v1;
+                num--;
+            } while (num);
+        }
+        // copy back the last processed vertex
+        c->vc.vBuffer[0] = *v0;
+        c->arrays.cull = v0->flags & vertex_t::CLIP_ALL;
+    } while (count);
+}
+
+void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (ggl_unlikely(count < 2))
+        return;
+    drawPrimitivesLineStrip(c, first, count);
+    if (ggl_likely(count >= 3)) {
+        vertex_t* v0 = c->vc.vBuffer; 
+        vertex_t* v1 = c->vc.vBuffer + 1;
+        c->arrays.compileElement(c, v1, first);
+        const uint32_t cc = v0->flags & v1->flags;
+        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+            c->prims.renderLine(c, v0, v1);
+    }
+}
+
+void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (ggl_unlikely(count < 2))
+        return;
+
+    // vertex cache size must be multiple of 2
+    const GLsizei vcs = 
+        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
+        vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2;
+    do {
+        vertex_t* v = c->vc.vBuffer;
+        GLsizei num = count > vcs ? vcs : count; 
+        c->arrays.cull = vertex_t::CLIP_ALL;
+        c->arrays.compileElements(c, v, first, num);
+        first += num;
+        count -= num;
+        if (!c->arrays.cull) {
+            // quick/trivial reject of the whole batch
+            num -= 2;
+            do {
+                const uint32_t cc = v[0].flags & v[1].flags;
+                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                    c->prims.renderLine(c, v, v+1);
+                v += 2;
+                num -= 2;
+            } while (num >= 0);
+        }
+    } while (count >= 2);
+}
+
+// ----------------------------------------------------------------------------
+
+static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c,
+        GLint first, GLsizei count, int winding)
+{
+    // winding == 2 : fan
+    // winding == 1 : strip
+
+    if (ggl_unlikely(count < 3))
+        return;
+
+    vertex_t *v, *v0, *v1, *v2;
+    c->arrays.cull = vertex_t::CLIP_ALL;
+    c->arrays.compileElements(c, c->vc.vBuffer, first, 2);
+    first += 2;
+    count -= 2;
+
+    // vertex cache size must be multiple of 2. This is extremely important
+    // because it allows us to preserve the same winding when the whole
+    // batch is culled. We also need 2 extra vertices in the array, because
+    // we always keep the two first ones.
+    const GLsizei vcs = 
+        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
+          vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2;
+    do {
+        v0 = c->vc.vBuffer + 0; 
+        v1 = c->vc.vBuffer + 1; 
+        v  = c->vc.vBuffer + 2;
+        GLsizei num = count > vcs ? vcs : count; 
+        c->arrays.compileElements(c, v, first, num);
+        first += num;
+        count -= num;
+        if (!c->arrays.cull) {
+            // quick/trivial reject of the whole batch
+            do {
+                v2 = v++;
+                const uint32_t cc = v0->flags & v1->flags & v2->flags;
+                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                    c->prims.renderTriangle(c, v0, v1, v2);
+                swap(((winding^=1) ? v1 : v0), v2);
+                num--;
+            } while (num);
+        }
+        if (count) {
+            v0 = c->vc.vBuffer + 2 + num - 2;
+            v1 = c->vc.vBuffer + 2 + num - 1;
+            if ((winding&2) == 0) {
+                // for strips copy back the two last compiled vertices
+                c->vc.vBuffer[0] = *v0;
+            }
+            c->vc.vBuffer[1] = *v1;
+            c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL;
+        }
+    } while (count > 0);
+}
+
+void drawPrimitivesTriangleStrip(ogles_context_t* c, 
+        GLint first, GLsizei count) {
+    drawPrimitivesTriangleFanOrStrip(c, first, count, 1);
+}
+
+void drawPrimitivesTriangleFan(ogles_context_t* c,
+        GLint first, GLsizei count) {
+    drawPrimitivesTriangleFanOrStrip(c, first, count, 2);
+}
+
+void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (ggl_unlikely(count < 3))
+        return;
+
+    // vertex cache size must be multiple of 3
+    const GLsizei vcs = 
+        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
+        vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3;
+    do {
+        vertex_t* v = c->vc.vBuffer;
+        GLsizei num = count > vcs ? vcs : count; 
+        c->arrays.cull = vertex_t::CLIP_ALL;
+        c->arrays.compileElements(c, v, first, num);
+        first += num;
+        count -= num;
+        if (!c->arrays.cull) {
+            // quick/trivial reject of the whole batch
+            num -= 3;
+            do {
+                const uint32_t cc = v[0].flags & v[1].flags & v[2].flags;
+                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                    c->prims.renderTriangle(c, v, v+1, v+2);
+                v += 3;
+                num -= 3;
+            } while (num >= 0);
+        }
+    } while (count >= 3);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+// this looks goofy, but gcc does a great job with this...
+static inline unsigned int read_index(int type, const GLvoid*& p) {
+    unsigned int r;
+    if (type) {
+        r = *(const GLubyte*)p;
+        p = (const GLubyte*)p + 1;
+    } else {
+        r = *(const GLushort*)p;
+        p = (const GLushort*)p + 1;
+    }
+    return r;
+}
+
+// ----------------------------------------------------------------------------
+
+void drawIndexedPrimitivesPoints(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices)
+{
+    if (ggl_unlikely(count < 1))
+        return;
+    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
+    do {
+        vertex_t * v = fetch_vertex(c, read_index(type, indices));
+        if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL)))
+            c->prims.renderPoint(c, v);
+        v->locked = 0;
+        count--;
+    } while(count);
+}
+
+// ----------------------------------------------------------------------------
+
+void drawIndexedPrimitivesLineStrip(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices)
+{
+    if (ggl_unlikely(count < 2))
+        return;
+    
+    vertex_t * const v = c->vc.vBuffer;
+    vertex_t* v0 = v;
+    vertex_t* v1;
+    
+    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
+    c->arrays.compileElement(c, v0, read_index(type, indices));
+    count -= 1;
+    do {
+        v1 = fetch_vertex(c, read_index(type, indices));
+        const uint32_t cc = v0->flags & v1->flags;
+        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+            c->prims.renderLine(c, v0, v1);
+        v0->locked = 0;
+        v0 = v1;
+        count--;
+    } while (count);
+    v1->locked = 0;
+}
+
+void drawIndexedPrimitivesLineLoop(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices)
+{
+    if (ggl_unlikely(count <= 2)) {
+        drawIndexedPrimitivesLines(c, count, indices);
+        return;
+    }
+ 
+    vertex_t * const v = c->vc.vBuffer;
+    vertex_t* v0 = v;
+    vertex_t* v1;
+    
+    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
+    c->arrays.compileElement(c, v0, read_index(type, indices));
+    count -= 1;
+    do {
+        v1 = fetch_vertex(c, read_index(type, indices));
+        const uint32_t cc = v0->flags & v1->flags;
+        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+            c->prims.renderLine(c, v0, v1);
+        v0->locked = 0;
+        v0 = v1;
+        count--;
+    } while (count);
+    v1->locked = 0;
+
+    v1 = c->vc.vBuffer; 
+    const uint32_t cc = v0->flags & v1->flags;
+    if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+        c->prims.renderLine(c, v0, v1);
+}
+
+void drawIndexedPrimitivesLines(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices)
+{
+    if (ggl_unlikely(count < 2))
+        return;
+
+    count -= 2;
+    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
+    do {
+        vertex_t* const v0 = fetch_vertex(c, read_index(type, indices));
+        vertex_t* const v1 = fetch_vertex(c, read_index(type, indices));
+        const uint32_t cc = v0->flags & v1->flags;
+        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+            c->prims.renderLine(c, v0, v1);
+        v0->locked = 0;
+        v1->locked = 0;
+        count -= 2;
+    } while (count >= 0);
+}
+
+// ----------------------------------------------------------------------------
+
+static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices, int winding)
+{
+    // winding == 2 : fan
+    // winding == 1 : strip
+
+    if (ggl_unlikely(count < 3))
+        return;
+    
+    vertex_t * const v = c->vc.vBuffer;
+    vertex_t* v0 = v;
+    vertex_t* v1 = v+1;
+    vertex_t* v2;
+
+    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
+    c->arrays.compileElement(c, v0, read_index(type, indices));
+    c->arrays.compileElement(c, v1, read_index(type, indices));
+    count -= 2;
+
+    // note: GCC 4.1.1 here makes a prety interesting optimization
+    // where it duplicates the loop below based on c->arrays.indicesType
+
+    do {
+        v2 = fetch_vertex(c, read_index(type, indices));
+        const uint32_t cc = v0->flags & v1->flags & v2->flags;
+        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+            c->prims.renderTriangle(c, v0, v1, v2);
+        vertex_t* & consumed = ((winding^=1) ? v1 : v0);
+        consumed->locked = 0;
+        consumed = v2;
+        count--;
+    } while (count);
+    v0->locked = v1->locked = 0;
+    v2->locked = 0;
+}
+
+void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices) {
+    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1);
+}
+
+void drawIndexedPrimitivesTriangleFan(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices) {
+    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2);
+}
+
+void drawIndexedPrimitivesTriangles(ogles_context_t* c,
+        GLsizei count, const GLvoid *indices)
+{
+    if (ggl_unlikely(count < 3))
+        return;
+
+    count -= 3;
+    if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) {
+        // This case is probably our most common case...
+        uint16_t const * p = (uint16_t const *)indices;
+        do {
+            vertex_t* const v0 = fetch_vertex(c, *p++);
+            vertex_t* const v1 = fetch_vertex(c, *p++);
+            vertex_t* const v2 = fetch_vertex(c, *p++);
+            const uint32_t cc = v0->flags & v1->flags & v2->flags;
+            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                c->prims.renderTriangle(c, v0, v1, v2);
+            v0->locked = 0;
+            v1->locked = 0;
+            v2->locked = 0;
+            count -= 3;
+        } while (count >= 0);
+    } else {
+        uint8_t const * p = (uint8_t const *)indices;
+        do {
+            vertex_t* const v0 = fetch_vertex(c, *p++);
+            vertex_t* const v1 = fetch_vertex(c, *p++);
+            vertex_t* const v2 = fetch_vertex(c, *p++);
+            const uint32_t cc = v0->flags & v1->flags & v2->flags;
+            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
+                c->prims.renderTriangle(c, v0, v1, v2);
+            v0->locked = 0;
+            v1->locked = 0;
+            v2->locked = 0;
+            count -= 3;
+        } while (count >= 0);
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Array compilers
+#endif
+
+void compileElement__generic(ogles_context_t* c,
+        vertex_t* v, GLint first)
+{
+    v->flags = 0;
+    v->index = first;
+    first &= vertex_cache_t::INDEX_MASK;
+    const GLubyte* vp = c->arrays.vertex.element(first);
+    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);
+}
+
+void compileElements__generic(ogles_context_t* c,
+        vertex_t* v, GLint first, GLsizei count)
+{
+    const GLubyte* vp = c->arrays.vertex.element(
+            first & vertex_cache_t::INDEX_MASK);
+    const size_t stride = c->arrays.vertex.stride;
+    transform_t const* const mvp = &c->transforms.mvp;
+    do {
+        v->flags = 0;
+        v->index = first++;
+        c->arrays.vertex.fetch(c, v->obj.v, vp);
+        c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
+        c->arrays.perspective(c, v);
+        vp += stride;
+        v++;
+    } while (--count);
+}
+
+/*
+void compileElements__3x_full(ogles_context_t* c,
+        vertex_t* v, GLint first, GLsizei count)
+{
+    const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first);
+    const size_t stride = c->arrays.vertex.stride / 4;
+//    const GLfixed* const& m = c->transforms.mvp.matrix.m;
+    
+    GLfixed m[16];
+    memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m));
+    
+    do {
+        const GLfixed rx = vp[0];
+        const GLfixed ry = vp[1];
+        const GLfixed rz = vp[2];
+        vp += stride;
+        v->index = first++;
+        v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+        v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+        v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+        v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
+
+        const GLfixed w = v->clip.w;
+        uint32_t clip = 0;
+        if (v->clip.x < -w)   clip |= vertex_t::CLIP_L;
+        if (v->clip.x >  w)   clip |= vertex_t::CLIP_R;
+        if (v->clip.y < -w)   clip |= vertex_t::CLIP_B;
+        if (v->clip.y >  w)   clip |= vertex_t::CLIP_T;
+        if (v->clip.z < -w)   clip |= vertex_t::CLIP_N;
+        if (v->clip.z >  w)   clip |= vertex_t::CLIP_F;
+        v->flags = clip;
+        c->arrays.cull &= clip;
+
+        //c->arrays.perspective(c, v);
+        v++;
+    } while (--count);
+}
+*/
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark clippers
+#endif
+
+static void clipVec4(vec4_t& nv, 
+        GLfixed t, const vec4_t& s, const vec4_t& p)
+{
+    for (int i=0; i<4 ; i++)
+        nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28);
+}
+
+static void clipVertex(ogles_context_t* c, vertex_t* nv,
+        GLfixed t, const vertex_t* s, const vertex_t* p)
+{
+    clipVec4(nv->clip, t, s->clip, p->clip);
+    nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28);
+    ogles_vertex_project(c, nv);
+    nv->flags |=  vertex_t::LIT | vertex_t::EYE | vertex_t::TT;
+    nv->flags &= ~vertex_t::CLIP_ALL;
+}
+
+static void clipVertexC(ogles_context_t* c, vertex_t* nv,
+        GLfixed t, const vertex_t* s, const vertex_t* p)
+{
+    clipVec4(nv->color, t, s->color, p->color);
+    clipVertex(c, nv, t, s, p);
+}
+
+static void clipVertexT(ogles_context_t* c, vertex_t* nv,
+        GLfixed t, const vertex_t* s, const vertex_t* p)
+{
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        if (c->rasterizer.state.texture[i].enable)
+            clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]);
+    }
+    clipVertex(c, nv, t, s, p);
+}
+
+static void clipVertexAll(ogles_context_t* c, vertex_t* nv,
+        GLfixed t, const vertex_t* s, const vertex_t* p)
+{
+    clipVec4(nv->color, t, s->color, p->color);
+    clipVertexT(c, nv, t, s, p);
+}
+
+static void clipEye(ogles_context_t* c, vertex_t* nv,
+        GLfixed t, const vertex_t* s, const vertex_t* p)
+{
+    nv->clear();
+    c->arrays.clipVertex(c, nv, t, p, s);
+    clipVec4(nv->eye, t, s->eye, p->eye);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void validate_arrays(ogles_context_t* c, GLenum mode)
+{
+    uint32_t enables = c->rasterizer.state.enables;
+
+    // Perspective correction is not need if Ortho transform, but
+    // the user can still provide the w coordinate manually, so we can't
+    // automatically turn it off (in fact we could when the 4th coordinate
+    // is not spcified in the vertex array).
+    // W interpolation is never needed for points.
+    GLboolean perspective = 
+        c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS);
+    c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective);
+    
+    // set anti-aliasing
+    GLboolean smooth = GL_FALSE;
+    switch (mode) {
+    case GL_POINTS:
+        smooth = c->point.smooth;
+        break;
+    case GL_LINES:
+    case GL_LINE_LOOP:
+    case GL_LINE_STRIP:
+        smooth = c->line.smooth;
+        break;
+    }
+    if (((enables & GGL_ENABLE_AA)?1:0) != smooth)
+        c->rasterizer.procs.enableDisable(c, GGL_AA, smooth);
+
+    // set the shade model for this primitive
+    c->rasterizer.procs.shadeModel(c,
+            (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel);
+
+    // compute all the matrices we'll need...
+    uint32_t want =
+            transform_state_t::MVP |
+            transform_state_t::VIEWPORT;
+    if (c->lighting.enable) { // needs normal transforms and eye coords
+        want |= transform_state_t::MVUI;
+        want |= transform_state_t::MODELVIEW;
+    }
+    if (enables & GGL_ENABLE_TMUS) { // needs texture transforms
+        want |= transform_state_t::TEXTURE;
+    }
+    if (c->clipPlanes.enable) { // needs eye coords
+        want |= transform_state_t::MODELVIEW;
+    }
+    ogles_validate_transform(c, want);
+
+    // textures...
+    if (enables & GGL_ENABLE_TMUS)
+        ogles_validate_texture(c);
+
+    // vertex compilers
+    c->arrays.compileElement = compileElement__generic;
+    c->arrays.compileElements = compileElements__generic;
+
+    // vertex transform
+    c->arrays.mvp_transform =
+        c->transforms.mvp.pointv[c->arrays.vertex.size - 2];
+
+    c->arrays.mv_transform =
+        c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2];
+    
+    /*
+     * ***********************************************************************
+     *  pick fetchers
+     * ***********************************************************************
+     */
+    
+    array_machine_t& am = c->arrays;
+    am.vertex.fetch = fetchNop;
+    am.normal.fetch = currentNormal;
+    am.color.fetch = currentColor;
+    
+    if (am.vertex.enable) {
+        am.vertex.resolve();
+        if (am.vertex.bo || am.vertex.pointer) {
+            am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF];
+        }
+    }
+
+    if (am.normal.enable) {
+        am.normal.resolve();
+        if (am.normal.bo || am.normal.pointer) {
+            am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF];
+        }
+    }
+
+    if (am.color.enable) {
+        am.color.resolve();
+        if (c->lighting.enable) {
+            if (am.color.bo || am.color.pointer) {
+                am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF];
+            }
+        } else {
+            if (am.color.bo || am.color.pointer) {
+                am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF];
+            }
+        }
+    }
+
+    int activeTmuCount = 0;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        am.texture[i].fetch = currentTexCoord;
+        if (c->rasterizer.state.texture[i].enable) {
+
+            // texture fetchers...
+            if (am.texture[i].enable) {
+                am.texture[i].resolve();
+                if (am.texture[i].bo || am.texture[i].pointer) {
+                    am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF];
+                }
+            }
+
+            // texture transform...
+            const int index = c->arrays.texture[i].size - 2;
+            c->arrays.tex_transform[i] =
+                c->transforms.texture[i].transform.pointv[index];
+
+            am.tmu = i;
+            activeTmuCount++;
+        }
+    }
+
+    // pick the vertex-clipper
+    uint32_t clipper = 0;
+    // we must reload 'enables' here
+    enables = c->rasterizer.state.enables;
+    if (enables & GGL_ENABLE_SMOOTH)
+        clipper |= 1;   // we need to interpolate colors
+    if (enables & GGL_ENABLE_TMUS)
+        clipper |= 2;   // we need to interpolate textures
+    switch (clipper) {
+    case 0: c->arrays.clipVertex = clipVertex;      break;
+    case 1: c->arrays.clipVertex = clipVertexC;     break;
+    case 2: c->arrays.clipVertex = clipVertexT;     break;
+    case 3: c->arrays.clipVertex = clipVertexAll;   break;
+    }
+    c->arrays.clipEye = clipEye;
+
+    // pick the primitive rasterizer
+    ogles_validate_primitives(c);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+#if 0
+#pragma mark -
+#pragma mark array API
+#endif
+
+void glVertexPointer(
+    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (size<2 || size>4 || stride<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (type) {
+    case GL_BYTE:
+    case GL_SHORT:
+    case GL_FIXED:
+    case GL_FLOAT:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
+}
+
+void glColorPointer(
+    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    // in theory ogles doesn't allow color arrays of size 3
+    // but it is very useful to 'visualize' the normal array.
+    if (size<3 || size>4 || stride<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (type) {
+    case GL_UNSIGNED_BYTE:
+    case GL_FIXED:
+    case GL_FLOAT:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
+}
+
+void glNormalPointer(
+    GLenum type, GLsizei stride, const GLvoid *pointer)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (stride<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (type) {
+    case GL_BYTE:
+    case GL_SHORT:
+    case GL_FIXED:
+    case GL_FLOAT:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0);
+}
+
+void glTexCoordPointer(
+    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (size<2 || size>4 || stride<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (type) {
+    case GL_BYTE:
+    case GL_SHORT:
+    case GL_FIXED:
+    case GL_FLOAT:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    const int tmu = c->arrays.activeTexture;
+    c->arrays.texture[tmu].init(size, type, stride, pointer,
+            c->arrays.array_buffer, 0);
+}
+
+
+void glEnableClientState(GLenum array) {
+    ogles_context_t* c = ogles_context_t::get();
+    enableDisableClientState(c, array, true);
+}
+
+void glDisableClientState(GLenum array) {
+    ogles_context_t* c = ogles_context_t::get();
+    enableDisableClientState(c, array, false);
+}
+
+void glClientActiveTexture(GLenum texture)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->arrays.activeTexture = texture - GL_TEXTURE0;
+}
+
+void glDrawArrays(GLenum mode, GLint first, GLsizei count)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (count<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (mode) {
+    case GL_POINTS:
+    case GL_LINE_STRIP:
+    case GL_LINE_LOOP:
+    case GL_LINES:
+    case GL_TRIANGLE_STRIP:
+    case GL_TRIANGLE_FAN:
+    case GL_TRIANGLES:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    if (count == 0 || !c->arrays.vertex.enable)
+        return;
+    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
+        return; // all triangles are culled
+
+    validate_arrays(c, mode);
+    drawArraysPrims[mode](c, first, count);
+
+#if VC_CACHE_STATISTICS
+    c->vc.total = count;
+    c->vc.dump_stats(mode);
+#endif
+}
+
+void glDrawElements(
+    GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (count<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    switch (mode) {
+    case GL_POINTS:
+    case GL_LINE_STRIP:
+    case GL_LINE_LOOP:
+    case GL_LINES:
+    case GL_TRIANGLE_STRIP:
+    case GL_TRIANGLE_FAN:
+    case GL_TRIANGLES:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    switch (type) {
+    case GL_UNSIGNED_BYTE:
+    case GL_UNSIGNED_SHORT:
+        c->arrays.indicesType = type;
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (count == 0 || !c->arrays.vertex.enable)
+        return;
+    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
+        return; // all triangles are culled
+
+    // clear the vertex-cache
+    c->vc.clear();
+    validate_arrays(c, mode);
+    
+    // if indices are in a buffer object, the pointer is treated as an
+    // offset in that buffer.
+    if (c->arrays.element_array_buffer) {
+        indices = c->arrays.element_array_buffer->data + uintptr_t(indices);
+    }
+
+    drawElementsPrims[mode](c, count, indices);
+
+#if VC_CACHE_STATISTICS
+    c->vc.total = count;
+    c->vc.dump_stats(mode);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// buffers
+// ----------------------------------------------------------------------------
+
+void glBindBuffer(GLenum target, GLuint buffer)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    // create a buffer object, or bind an existing one
+    buffer_t const* bo = 0;
+    if (buffer) {
+        bo = c->bufferObjectManager->bind(buffer);
+        if (!bo) {
+            ogles_error(c, GL_OUT_OF_MEMORY);
+            return;
+        }
+    }
+    ((target == GL_ARRAY_BUFFER) ? 
+            c->arrays.array_buffer : c->arrays.element_array_buffer) = bo;
+}
+
+void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (size<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? 
+            c->arrays.array_buffer : c->arrays.element_array_buffer);
+
+    if (bo == 0) {
+        // can't modify buffer 0
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+
+    buffer_t* edit_bo = const_cast<buffer_t*>(bo);
+    if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) {
+        ogles_error(c, GL_OUT_OF_MEMORY);
+        return;
+    }
+    if (data) {
+        memcpy(bo->data, data, size);
+    }
+}
+
+void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (offset<0 || size<0 || data==0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? 
+            c->arrays.array_buffer : c->arrays.element_array_buffer);
+
+    if (bo == 0) {
+        // can't modify buffer 0
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+    if (offset+size > bo->size) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    memcpy(bo->data + offset, data, size);
+}
+
+void glDeleteBuffers(GLsizei n, const GLuint* buffers)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (n<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    for (int i=0 ; i<n ; i++) {
+        GLuint name = buffers[i];
+        if (name) {
+            // unbind bound deleted buffers...
+            if (c->arrays.element_array_buffer->name == name) {
+                c->arrays.element_array_buffer = 0;
+            }
+            if (c->arrays.array_buffer->name == name) {
+                c->arrays.array_buffer = 0;
+            }
+            if (c->arrays.vertex.bo->name == name) {
+                c->arrays.vertex.bo = 0;
+            }
+            if (c->arrays.normal.bo->name == name) {
+                c->arrays.normal.bo = 0;
+            }
+            if (c->arrays.color.bo->name == name) {
+                c->arrays.color.bo = 0;
+            }
+            for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
+                if (c->arrays.texture[t].bo->name == name) {
+                    c->arrays.texture[t].bo = 0;
+                }
+            }
+        }
+    }    
+    c->bufferObjectManager->deleteBuffers(n, buffers);
+    c->bufferObjectManager->recycleTokens(n, buffers);
+}
+
+void glGenBuffers(GLsizei n, GLuint* buffers)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (n<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    c->bufferObjectManager->getToken(n, buffers);
+}
diff --git a/opengl/libagl/array.h b/opengl/libagl/array.h
new file mode 100644
index 0000000..e156978
--- /dev/null
+++ b/opengl/libagl/array.h
@@ -0,0 +1,37 @@
+/* libs/opengles/array.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_ARRAY_H
+#define ANDROID_OPENGLES_ARRAY_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace gl {
+struct ogles_context_t;
+};
+
+void ogles_init_array(ogles_context_t* c);
+void ogles_uninit_array(ogles_context_t* c);
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_ARRAY_H
+
diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h
new file mode 100644
index 0000000..ef36b56
--- /dev/null
+++ b/opengl/libagl/context.h
@@ -0,0 +1,20 @@
+/* libs/opengles/context.h
+**
+** Copyright 2006, 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.
+*/
+
+#include <private/opengles/gl_context.h>
+
+using namespace android::gl;
diff --git a/opengl/libagl/dxt.cpp b/opengl/libagl/dxt.cpp
new file mode 100644
index 0000000..238c81f
--- /dev/null
+++ b/opengl/libagl/dxt.cpp
@@ -0,0 +1,636 @@
+/* libs/opengles/dxt.cpp
+**
+** Copyright 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.
+*/
+
+#define TIMING 0
+
+#if TIMING
+#include <sys/time.h> // for optimization timing
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#include <GLES/gl.h>
+#include <utils/Endian.h>
+
+#include "context.h"
+
+#define TIMING 0
+
+namespace android {
+
+static uint8_t avg23tab[64*64];
+static volatile int tables_initialized = 0;
+
+// Definitions below are equivalent to these over the valid range of arguments
+//  #define div5(x) ((x)/5)
+//  #define div7(x) ((x)/7)
+
+// Use fixed-point to divide by 5 and 7
+// 3277 = 2^14/5 + 1
+// 2341 = 2^14/7 + 1
+#define div5(x) (((x)*3277) >> 14)
+#define div7(x) (((x)*2341) >> 14)
+
+// Table with entry [a << 6 | b] = (2*a + b)/3 for 0 <= a,b < 64
+#define avg23(x0,x1) avg23tab[((x0) << 6) | (x1)]
+
+// Extract 5/6/5 RGB
+#define red(x)   (((x) >> 11) & 0x1f)
+#define green(x) (((x) >>  5) & 0x3f)
+#define blue(x)  ( (x)        & 0x1f)
+
+/*
+ * Convert 5/6/5 RGB (as 3 ints) to 8/8/8
+ *
+ * Operation count: 8 <<, 0 &, 5 |
+ */
+inline static int rgb565SepTo888(int r, int g, int b)
+
+{
+    return ((((r << 3) | (r >> 2)) << 16) |
+            (((g << 2) | (g >> 4)) <<  8) |
+             ((b << 3) | (b >> 2)));
+}
+
+/*
+ * Convert 5/6/5 RGB (as a single 16-bit word) to 8/8/8
+ *
+ *                   r4r3r2r1 r0g5g4g3 g2g1g0b4 b3b2b1b0   rgb
+ *            r4r3r2 r1r0g5g4 g3g2g1g0 b4b3b2b1 b0 0 0 0   rgb << 3
+ * r4r3r2r1 r0r4r3r2 g5g4g3g2 g1g0g5g4 b4b3b2b1 b0b4b3b2   desired result
+ *
+ * Construct the 24-bit RGB word as:
+ *
+ * r4r3r2r1 r0------ -------- -------- -------- --------  (rgb << 8) & 0xf80000
+ *            r4r3r2 -------- -------- -------- --------  (rgb << 3) & 0x070000
+ *                   g5g4g3g2 g1g0---- -------- --------  (rgb << 5) & 0x00fc00
+ *                                g5g4 -------- --------  (rgb >> 1) & 0x000300
+ *                                     b4b3b2b1 b0------  (rgb << 3) & 0x0000f8
+ *                                                b4b3b2  (rgb >> 2) & 0x000007
+ *
+ * Operation count: 5 <<, 6 &, 5 | (n.b. rgb >> 3 is used twice)
+ */
+inline static int rgb565To888(int rgb)
+
+{
+    int rgb3 = rgb >> 3;
+    return (((rgb << 8) & 0xf80000) |
+            ( rgb3      & 0x070000) |
+            ((rgb << 5) & 0x00fc00) |
+            ((rgb >> 1) & 0x000300) |
+            ( rgb3      & 0x0000f8) |
+            ((rgb >> 2) & 0x000007));
+}
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint32_t swap(uint32_t x) {
+    int b0 = (x >> 24) & 0xff;
+    int b1 = (x >> 16) & 0xff;
+    int b2 = (x >>  8) & 0xff;
+    int b3 = (x      ) & 0xff;
+    
+    return (uint32_t)((b3 << 24) | (b2 << 16) | (b1 << 8) | b0);
+}
+#endif
+
+static void
+init_tables()
+{
+    if (tables_initialized) {
+        return;
+    }
+
+    for (int i = 0; i < 64; i++) {
+        for (int j = 0; j < 64; j++) {
+            int avg = (2*i + j)/3;
+            avg23tab[(i << 6) | j] = avg;
+        }
+    }
+
+    asm volatile ("" : : : "memory");
+    tables_initialized = 1;
+}
+
+/*
+ * Utility to scan a DXT1 compressed texture to determine whether it
+ * contains a transparent pixel (color0 < color1, code == 3).  This
+ * may be useful if the application lacks information as to whether
+ * the true format is GL_COMPRESSED_RGB_S3TC_DXT1_EXT or
+ * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT.
+ */
+bool
+DXT1HasAlpha(const GLvoid *data, int width, int height) {    
+#if TIMING
+    struct timeval start_t, end_t;
+    struct timezone tz;
+    
+    gettimeofday(&start_t, &tz);
+#endif
+
+    bool hasAlpha = false;
+
+    int xblocks = (width + 3)/4;
+    int yblocks = (height + 3)/4;
+    int numblocks = xblocks*yblocks;
+
+    uint32_t const *d32 = (uint32_t *)data;
+    for (int b = 0; b < numblocks; b++) {
+        uint32_t colors = *d32++;
+        
+#if __BYTE_ORDER == __BIG_ENDIAN
+        colors = swap(colors);
+#endif
+        
+        uint16_t color0 = colors & 0xffff;
+        uint16_t color1 = colors >> 16;
+        
+        if (color0 < color1) {
+            // There's no need to endian-swap within 'bits'
+            // since we don't care which pixel is the transparent one
+            uint32_t bits = *d32++;
+            
+            // Detect if any (odd, even) pair of bits are '11'
+            //      bits: b31 b30 b29 ... b3 b2 b1 b0
+            // bits >> 1: b31 b31 b30 ... b4 b3 b2 b1
+            //         &: b31 (b31 & b30) (b29 & b28) ... (b2 & b1) (b1 & b0)
+            //  & 0x55..:   0 (b31 & b30)       0     ...     0     (b1 & b0)
+            if (((bits & (bits >> 1)) & 0x55555555) != 0) {
+                hasAlpha = true;
+                goto done;
+            }
+        } else {
+            // Skip 4 bytes
+            ++d32;
+        }
+    }
+    
+ done:
+#if TIMING
+    gettimeofday(&end_t, &tz);
+    long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 +
+        (end_t.tv_usec - start_t.tv_usec);
+    
+    printf("Scanned w=%d h=%d in %ld usec\n", width, height, usec);
+#endif
+    
+    return hasAlpha;
+}
+
+static void
+decodeDXT1(const GLvoid *data, int width, int height,
+           void *surface, int stride,
+           bool hasAlpha)
+    
+{
+    init_tables();
+    
+    uint32_t const *d32 = (uint32_t *)data;
+    
+    // Color table for the current block
+    uint16_t c[4];
+    c[0] = c[1] = c[2] = c[3] = 0;
+    
+    // Specified colors from the previous block
+    uint16_t prev_color0 = 0x0000;
+    uint16_t prev_color1 = 0x0000;
+    
+    uint16_t* rowPtr = (uint16_t*)surface;
+    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
+        uint16_t *blockPtr = rowPtr;
+        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
+            uint32_t colors = *d32++;
+            uint32_t bits = *d32++;
+            
+#if __BYTE_ORDER == __BIG_ENDIAN
+            colors = swap(colors);
+            bits = swap(bits);
+#endif
+            
+            // Raw colors
+            uint16_t color0 = colors & 0xffff;
+            uint16_t color1 = colors >> 16;
+            
+            // If the new block has the same base colors as the
+            // previous one, we don't need to recompute the color
+            // table c[]
+            if (color0 != prev_color0 || color1 != prev_color1) {
+                // Store raw colors for comparison with next block
+                prev_color0 = color0;
+                prev_color1 = color1;
+                
+                int r0 =   red(color0);
+                int g0 = green(color0);
+                int b0 =  blue(color0);
+
+                int r1 =   red(color1);
+                int g1 = green(color1);
+                int b1 =  blue(color1);                
+                
+                if (hasAlpha) {
+                    c[0] = (r0 << 11) | ((g0 >> 1) << 6) | (b0 << 1) | 0x1;
+                    c[1] = (r1 << 11) | ((g1 >> 1) << 6) | (b1 << 1) | 0x1;
+                } else {
+                    c[0] = color0;
+                    c[1] = color1;
+                }
+                
+                int r2, g2, b2, r3, g3, b3, a3;
+                
+                int bbits = bits >> 1;
+                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
+                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
+                
+                if (has2 || has3) {
+                    if (color0 > color1) {
+                        r2 = avg23(r0, r1);
+                        g2 = avg23(g0, g1);
+                        b2 = avg23(b0, b1);
+                        
+                        r3 = avg23(r1, r0);
+                        g3 = avg23(g1, g0);
+                        b3 = avg23(b1, b0);
+                        a3 = 1;
+                    } else {
+                        r2 = (r0 + r1) >> 1;
+                        g2 = (g0 + g1) >> 1;
+                        b2 = (b0 + b1) >> 1;
+                        
+                        r3 = g3 = b3 = a3 = 0;
+                    }
+                    if (hasAlpha) {
+                        c[2] = (r2 << 11) | ((g2 >> 1) << 6) |
+                            (b2 << 1) | 0x1;
+                        c[3] = (r3 << 11) | ((g3 >> 1) << 6) |
+                            (b3 << 1) | a3;
+                    } else {
+                        c[2] = (r2 << 11) | (g2 << 5) | b2;
+                        c[3] = (r3 << 11) | (g3 << 5) | b3;
+                    }
+                }
+            }
+            
+            uint16_t* blockRowPtr = blockPtr;
+            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
+                // Don't process rows past the botom
+                if (base_y + y >= height) {
+                    break;
+                }
+                
+                int w = min(width - base_x, 4);
+                for (int x = 0; x < w; x++) {
+                    int code = bits & 0x3;
+                    bits >>= 2;
+                    
+                    blockRowPtr[x] = c[code];
+                }
+            }
+        }
+    }
+}
+    
+// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE
+static void
+decodeDXT3(const GLvoid *data, int width, int height,
+           void *surface, int stride)
+
+{
+    init_tables();
+    
+    uint32_t const *d32 = (uint32_t *)data;
+    
+    // Specified colors from the previous block
+    uint16_t prev_color0 = 0x0000;
+    uint16_t prev_color1 = 0x0000;
+
+    // Color table for the current block
+    uint32_t c[4];
+    c[0] = c[1] = c[2] = c[3] = 0;
+
+    uint32_t* rowPtr = (uint32_t*)surface;
+    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
+        uint32_t *blockPtr = rowPtr;
+        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
+            
+#if __BYTE_ORDER == __BIG_ENDIAN
+            uint32_t alphahi = *d32++;
+            uint32_t alphalo = *d32++;
+            alphahi = swap(alphahi);
+            alphalo = swap(alphalo);
+#else
+            uint32_t alphalo = *d32++;
+            uint32_t alphahi = *d32++;
+#endif
+
+            uint32_t colors = *d32++;
+            uint32_t bits = *d32++;
+            
+#if __BYTE_ORDER == __BIG_ENDIAN
+            colors = swap(colors);
+            bits = swap(bits);
+#endif
+            
+            uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo;
+
+            // Raw colors
+            uint16_t color0 = colors & 0xffff;
+            uint16_t color1 = colors >> 16;
+
+            // If the new block has the same base colors as the
+            // previous one, we don't need to recompute the color
+            // table c[]
+            if (color0 != prev_color0 || color1 != prev_color1) {
+                // Store raw colors for comparison with next block
+                prev_color0 = color0;
+                prev_color1 = color1;
+                
+                int bbits = bits >> 1;
+                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
+                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
+                
+                if (has2 || has3) {
+                    int r0 =   red(color0);
+                    int g0 = green(color0);
+                    int b0 =  blue(color0);
+                    
+                    int r1 =   red(color1);
+                    int g1 = green(color1);
+                    int b1 =  blue(color1);
+                    
+                    int r2 = avg23(r0, r1);
+                    int g2 = avg23(g0, g1);
+                    int b2 = avg23(b0, b1);
+                    
+                    int r3 = avg23(r1, r0);
+                    int g3 = avg23(g1, g0);
+                    int b3 = avg23(b1, b0);
+
+                    c[0] = rgb565SepTo888(r0, g0, b0);
+                    c[1] = rgb565SepTo888(r1, g1, b1);
+                    c[2] = rgb565SepTo888(r2, g2, b2);
+                    c[3] = rgb565SepTo888(r3, g3, b3);
+                } else {
+                    // Convert to 8 bits
+                    c[0] = rgb565To888(color0);
+                    c[1] = rgb565To888(color1);
+                }
+            }
+
+            uint32_t* blockRowPtr = blockPtr;
+            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
+                // Don't process rows past the botom
+                if (base_y + y >= height) {
+                    break;
+                }
+                
+                int w = min(width - base_x, 4);
+                for (int x = 0; x < w; x++) {
+                    int a = alpha & 0xf;
+                    alpha >>= 4;
+
+                    int code = bits & 0x3;
+                    bits >>= 2;
+
+                    blockRowPtr[x] = c[code] | (a << 28) | (a << 24);
+                }
+            }
+        }
+    }
+}
+
+// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE
+static void
+decodeDXT5(const GLvoid *data, int width, int height,
+           void *surface, int stride)
+
+{
+    init_tables();
+    
+    uint32_t const *d32 = (uint32_t *)data;
+    
+    // Specified alphas from the previous block
+    uint8_t prev_alpha0 = 0x00;
+    uint8_t prev_alpha1 = 0x00;
+
+    // Specified colors from the previous block
+    uint16_t prev_color0 = 0x0000;
+     uint16_t prev_color1 = 0x0000;
+
+    // Alpha table for the current block
+    uint8_t a[8];
+    a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = 0;
+
+    // Color table for the current block
+    uint32_t c[4];
+    c[0] = c[1] = c[2] = c[3] = 0;
+
+    int good_a5 = 0;
+    int bad_a5 = 0;
+    int good_a6 = 0;
+    int bad_a6 = 0;
+    int good_a7 = 0;
+    int bad_a7 = 0;
+
+    uint32_t* rowPtr = (uint32_t*)surface;
+    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
+        uint32_t *blockPtr = rowPtr;
+        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
+            
+#if __BYTE_ORDER == __BIG_ENDIAN
+            uint32_t alphahi = *d32++;
+            uint32_t alphalo = *d32++;
+            alphahi = swap(alphahi);
+            alphalo = swap(alphalo);
+#else
+             uint32_t alphalo = *d32++;
+             uint32_t alphahi = *d32++;
+#endif
+
+            uint32_t colors = *d32++;
+            uint32_t bits = *d32++;
+            
+#if __BYTE_ORDER == __BIG_ENDIANx
+            colors = swap(colors);
+            bits = swap(bits);
+#endif
+            
+            uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo;
+            uint64_t alpha0 = alpha & 0xff;
+            alpha >>= 8;
+            uint64_t alpha1 = alpha & 0xff;
+            alpha >>= 8;
+
+            if (alpha0 != prev_alpha0 || alpha1 != prev_alpha1) {
+                prev_alpha0 = alpha0;
+                prev_alpha1 = alpha1;
+                
+                a[0] = alpha0;
+                a[1] = alpha1;
+                int a01 = alpha0 + alpha1 - 1;
+                if (alpha0 > alpha1) {
+                    a[2] = div7(6*alpha0 +   alpha1);
+                    a[4] = div7(4*alpha0 + 3*alpha1);
+                    a[6] = div7(2*alpha0 + 5*alpha1);
+
+                    // Use symmetry to derive half of the values
+                    // A few values will be off by 1 (~.5%)
+                    // Alternate which values are computed directly
+                    // and which are derived to try to reduce bias
+                    a[3] = a01 - a[6];
+                    a[5] = a01 - a[4];
+                    a[7] = a01 - a[2];
+                } else {
+                    a[2] = div5(4*alpha0 +   alpha1);
+                    a[4] = div5(2*alpha0 + 3*alpha1);
+                    a[3] = a01 - a[4];
+                    a[5] = a01 - a[2];
+                    a[6] = 0x00;
+                    a[7] = 0xff;
+                }
+            }
+
+            // Raw colors
+            uint16_t color0 = colors & 0xffff;
+            uint16_t color1 = colors >> 16;
+
+            // If the new block has the same base colors as the
+            // previous one, we don't need to recompute the color
+            // table c[]
+            if (color0 != prev_color0 || color1 != prev_color1) {
+                // Store raw colors for comparison with next block
+                prev_color0 = color0;
+                prev_color1 = color1;
+                
+                int bbits = bits >> 1;
+                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
+                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
+                
+                if (has2 || has3) {
+                    int r0 =   red(color0);
+                    int g0 = green(color0);
+                    int b0 =  blue(color0);
+                    
+                    int r1 =   red(color1);
+                    int g1 = green(color1);
+                    int b1 =  blue(color1);
+                
+                    int r2 = avg23(r0, r1);
+                    int g2 = avg23(g0, g1);
+                    int b2 = avg23(b0, b1);
+                    
+                    int r3 = avg23(r1, r0);
+                    int g3 = avg23(g1, g0);
+                    int b3 = avg23(b1, b0);
+
+                    c[0] = rgb565SepTo888(r0, g0, b0);
+                    c[1] = rgb565SepTo888(r1, g1, b1);
+                    c[2] = rgb565SepTo888(r2, g2, b2);
+                    c[3] = rgb565SepTo888(r3, g3, b3);
+                } else {
+                    // Convert to 8 bits
+                    c[0] = rgb565To888(color0);
+                    c[1] = rgb565To888(color1);
+                }                
+            }
+
+            uint32_t* blockRowPtr = blockPtr;
+            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
+                // Don't process rows past the botom
+                if (base_y + y >= height) {
+                    break;
+                }
+                
+                int w = min(width - base_x, 4);
+                for (int x = 0; x < w; x++) {
+                    int acode = alpha & 0x7;
+                    alpha >>= 3;
+
+                    int code = bits & 0x3;
+                    bits >>= 2;
+
+                    blockRowPtr[x] = c[code] | (a[acode] << 24);
+                }
+            }
+        }
+    }
+}
+   
+/*
+ * Decode a DXT-compressed texture into memory.  DXT textures consist of
+ * a series of 4x4 pixel blocks in left-to-right, top-down order.
+ * The number of blocks is given by ceil(width/4)*ceil(height/4).
+ *
+ * 'data' points to the texture data. 'width' and 'height' indicate the
+ * dimensions of the texture.  We assume width and height are >= 0 but
+ * do not require them to be powers of 2 or divisible by any factor.
+ *
+ * The output is written to 'surface' with each scanline separated by
+ * 'stride' 2- or 4-byte words.
+ *
+ * 'format' indicates the type of compression and must be one of the following:
+ *
+ *   GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ *      The output is written as 5/6/5 opaque RGB (16 bit words).
+ *      8 bytes are read from 'data' for each block.
+ *
+ *   GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+ *      The output is written as 5/5/5/1 RGBA (16 bit words)
+ *      8 bytes are read from 'data' for each block.
+ *
+ *   GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+ *   GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+ *      The output is written as 8/8/8/8 ARGB (32 bit words)
+ *      16 bytes are read from 'data' for each block.
+ */
+void
+decodeDXT(const GLvoid *data, int width, int height,
+          void *surface, int stride, int format)
+{
+#if TIMING
+    struct timeval start_t, end_t;
+    struct timezone tz;
+    
+    gettimeofday(&start_t, &tz);
+#endif
+
+    switch (format) {
+    case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+        decodeDXT1(data, width, height, surface, stride, false);
+        break;
+        
+    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+        decodeDXT1(data, width, height, surface, stride, true);
+        break;
+        
+    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+        decodeDXT3(data, width, height, surface, stride);
+        break;
+        
+    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+        decodeDXT5(data, width, height, surface, stride);
+        break;
+    }
+    
+#if TIMING
+    gettimeofday(&end_t, &tz);
+    long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 +
+        (end_t.tv_usec - start_t.tv_usec);
+    
+    printf("Loaded w=%d h=%d in %ld usec\n", width, height, usec);
+#endif
+}
+
+} // namespace android
diff --git a/opengl/libagl/dxt.h b/opengl/libagl/dxt.h
new file mode 100644
index 0000000..d95a36c
--- /dev/null
+++ b/opengl/libagl/dxt.h
@@ -0,0 +1,33 @@
+/* libs/opengles/dxt.h
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_OPENGLES_TEXTURE_H
+#define ANDROID_OPENGLES_TEXTURE_H
+
+#include <stdlib.h>
+
+#include <GLES/gl.h>
+
+namespace android {
+
+  bool DXT1HasAlpha(const GLvoid *data, int width, int height);
+  void decodeDXT(const GLvoid *data, int width, int height,
+                 void *surface, int stride, int format);
+
+} // namespace android
+
+#endif // ANDROID_OPENGLES_TEXTURE_H
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
new file mode 100644
index 0000000..854a49e
--- /dev/null
+++ b/opengl/libagl/egl.cpp
@@ -0,0 +1,1513 @@
+/* 
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "EGL"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include <utils/threads.h>
+
+#include <GLES/egl.h>
+
+#include <pixelflinger/format.h>
+
+#include "context.h"
+#include "state.h"
+#include "texture.h"
+#include "matrix.h"
+
+#undef NELEM
+#define NELEM(x) (sizeof(x)/sizeof(*(x)))
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+const unsigned int NUM_DISPLAYS = 1;
+
+static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_key_t gEGLErrorKey = -1;
+#ifndef HAVE_ANDROID_OS
+namespace gl {
+pthread_key_t gGLKey = -1;
+}; // namspace gl
+#endif
+
+template<typename T>
+static T setError(GLint error, T returnValue) {
+    if (ggl_unlikely(gEGLErrorKey == -1)) {
+        pthread_mutex_lock(&gErrorKeyMutex);
+        if (gEGLErrorKey == -1)
+            pthread_key_create(&gEGLErrorKey, NULL);
+        pthread_mutex_unlock(&gErrorKeyMutex);
+    }
+    pthread_setspecific(gEGLErrorKey, (void*)error);
+    return returnValue;
+}
+
+static GLint getError() {
+    if (ggl_unlikely(gEGLErrorKey == -1))
+        return EGL_SUCCESS;
+    GLint error = (GLint)pthread_getspecific(gEGLErrorKey);
+    pthread_setspecific(gEGLErrorKey, (void*)EGL_SUCCESS);
+    return error;
+}
+
+// ----------------------------------------------------------------------------
+
+struct egl_display_t
+{
+    egl_display_t() : type(0), initialized(0) { }
+    
+    static egl_display_t& get_display(EGLDisplay dpy);
+    
+    static EGLBoolean is_valid(EGLDisplay dpy) {
+        return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE;
+    }
+
+    NativeDisplayType   type;
+    volatile int32_t    initialized;
+};
+
+static egl_display_t gDisplays[NUM_DISPLAYS];
+
+egl_display_t& egl_display_t::get_display(EGLDisplay dpy) {
+    return gDisplays[uintptr_t(dpy)-1U];
+}
+
+struct egl_context_t {
+    enum {
+        IS_CURRENT      =   0x00010000,
+        NEVER_CURRENT   =   0x00020000
+    };
+    uint32_t            flags;
+    EGLDisplay          dpy;
+    EGLConfig           config;
+    EGLSurface          read;
+    EGLSurface          draw;
+
+    static inline egl_context_t* context(EGLContext ctx) {
+        ogles_context_t* const gl = static_cast<ogles_context_t*>(ctx);
+        return static_cast<egl_context_t*>(gl->rasterizer.base);
+    }
+};
+
+// ----------------------------------------------------------------------------
+
+struct egl_surface_t
+{
+    enum {
+        PAGE_FLIP = 0x00000001,
+        MAGIC     = 0x31415265
+    };
+
+    uint32_t            magic;
+    EGLDisplay          dpy;
+    EGLConfig           config;
+    EGLContext          ctx;
+
+                egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat);
+    virtual     ~egl_surface_t();
+    virtual     bool    isValid() const = 0;
+    
+    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl) = 0;
+    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl) = 0;
+    virtual     EGLint      getWidth() const = 0;
+    virtual     EGLint      getHeight() const = 0;
+    virtual     void*       getBits() const = 0;
+
+    virtual     EGLint      getHorizontalResolution() const;
+    virtual     EGLint      getVerticalResolution() const;
+    virtual     EGLint      getRefreshRate() const;
+    virtual     EGLint      getSwapBehavior() const;
+    virtual     EGLBoolean  swapBuffers();
+    virtual     EGLBoolean  swapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
+protected:
+    GGLSurface              depth;
+};
+
+egl_surface_t::egl_surface_t(EGLDisplay dpy,
+        EGLConfig config,
+        int32_t depthFormat)
+    : magic(0x31415265), dpy(dpy), config(config), ctx(0)
+{
+    depth.version = sizeof(GGLSurface);
+    depth.data = 0;
+    depth.format = depthFormat;
+}
+egl_surface_t::~egl_surface_t()
+{
+    magic = 0;
+    free(depth.data);
+}
+EGLBoolean egl_surface_t::swapBuffers() {
+    return EGL_FALSE;
+}
+EGLBoolean egl_surface_t::swapRectangle(
+        EGLint l, EGLint t, EGLint w, EGLint h) {
+    return EGL_FALSE;
+}
+EGLint egl_surface_t::getHorizontalResolution() const {
+    return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
+}
+EGLint egl_surface_t::getVerticalResolution() const {
+    return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
+}
+EGLint egl_surface_t::getRefreshRate() const {
+    return (60 * EGL_DISPLAY_SCALING);
+}
+EGLint egl_surface_t::getSwapBehavior() const {
+    return EGL_BUFFER_PRESERVED;
+}
+
+// ----------------------------------------------------------------------------
+
+struct egl_window_surface_t : public egl_surface_t
+{
+    egl_window_surface_t(
+            EGLDisplay dpy, EGLConfig config,
+            int32_t depthFormat,
+            egl_native_window_t* window);
+
+     ~egl_window_surface_t();
+
+    virtual     bool        isValid() const { return nativeWindow->magic == 0x600913; }    
+    virtual     EGLBoolean  swapBuffers();
+    virtual     EGLBoolean  swapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
+    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
+    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
+    virtual     EGLint      getWidth() const    { return nativeWindow->width;  }
+    virtual     EGLint      getHeight() const   { return nativeWindow->height; }
+    virtual     void*       getBits() const;
+    virtual     EGLint      getHorizontalResolution() const;
+    virtual     EGLint      getVerticalResolution() const;
+    virtual     EGLint      getRefreshRate() const;
+    virtual     EGLint      getSwapBehavior() const;
+private:
+    egl_native_window_t*    nativeWindow;
+};
+
+egl_window_surface_t::egl_window_surface_t(EGLDisplay dpy,
+        EGLConfig config,
+        int32_t depthFormat,
+        egl_native_window_t* window)
+    : egl_surface_t(dpy, config, depthFormat), nativeWindow(window)
+{
+    if (depthFormat) {
+        depth.width   = window->width;
+        depth.height  = window->height;
+        depth.stride  = depth.width; // use the width here
+        depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+        if (depth.data == 0) {
+            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+            return;
+        }
+    }
+    nativeWindow->incRef(nativeWindow);
+}
+egl_window_surface_t::~egl_window_surface_t() {
+    nativeWindow->decRef(nativeWindow);
+}
+
+EGLBoolean egl_window_surface_t::swapBuffers()
+{
+    uint32_t flags = nativeWindow->swapBuffers(nativeWindow);
+    if (flags & EGL_NATIVES_FLAG_SIZE_CHANGED) {
+        // TODO: we probaly should reset the swap rect here
+        // if the window size has changed
+        //    window->setSwapRectangle(Rect(info.w, info.h));
+        if (depth.data) {
+            free(depth.data);
+            depth.width   = nativeWindow->width;
+            depth.height  = nativeWindow->height;
+            depth.stride  = nativeWindow->stride;
+            depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+            if (depth.data == 0) {
+                setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+                return EGL_FALSE;
+            }
+        }
+    }
+    return EGL_TRUE;
+}
+
+EGLBoolean egl_window_surface_t::swapRectangle(
+        EGLint l, EGLint t, EGLint w, EGLint h)
+{
+    nativeWindow->setSwapRectangle(nativeWindow, l, t, w, h);
+    return EGL_TRUE;
+}
+EGLBoolean egl_window_surface_t::bindDrawSurface(ogles_context_t* gl)
+{
+    GGLSurface buffer;
+    buffer.version = sizeof(GGLSurface);
+    buffer.width   = nativeWindow->width;
+    buffer.height  = nativeWindow->height;
+    buffer.stride  = nativeWindow->stride;
+    buffer.data    = (GGLubyte*)nativeWindow->base + nativeWindow->offset;
+    buffer.format  = nativeWindow->format;
+    gl->rasterizer.procs.colorBuffer(gl, &buffer);
+    if (depth.data != gl->rasterizer.state.buffers.depth.data)
+        gl->rasterizer.procs.depthBuffer(gl, &depth);
+    return EGL_TRUE;
+}
+EGLBoolean egl_window_surface_t::bindReadSurface(ogles_context_t* gl)
+{
+    GGLSurface buffer;
+    buffer.version = sizeof(GGLSurface);
+    buffer.width   = nativeWindow->width;
+    buffer.height  = nativeWindow->height;
+    buffer.stride  = nativeWindow->stride;
+    buffer.data    = (GGLubyte*)nativeWindow->base + nativeWindow->offset;
+    buffer.format  = nativeWindow->format;
+    gl->rasterizer.procs.readBuffer(gl, &buffer);
+    return EGL_TRUE;
+}
+void* egl_window_surface_t::getBits() const {
+    return (GGLubyte*)nativeWindow->base + nativeWindow->offset;
+}
+EGLint egl_window_surface_t::getHorizontalResolution() const {
+    return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
+}
+EGLint egl_window_surface_t::getVerticalResolution() const {
+    return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
+}
+EGLint egl_window_surface_t::getRefreshRate() const {
+    return (nativeWindow->fps * EGL_DISPLAY_SCALING);
+}
+EGLint egl_window_surface_t::getSwapBehavior() const {
+    uint32_t flags = nativeWindow->flags;
+    if (flags & EGL_NATIVES_FLAG_DESTROY_BACKBUFFER)
+        return EGL_BUFFER_DESTROYED;
+    return EGL_BUFFER_PRESERVED;
+}
+
+// ----------------------------------------------------------------------------
+
+struct egl_pixmap_surface_t : public egl_surface_t
+{
+    egl_pixmap_surface_t(
+            EGLDisplay dpy, EGLConfig config,
+            int32_t depthFormat,
+            egl_native_pixmap_t const * pixmap);
+
+    virtual ~egl_pixmap_surface_t() { }
+
+    virtual     bool        isValid() const { return nativePixmap.version == sizeof(egl_native_pixmap_t); }    
+    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
+    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
+    virtual     EGLint      getWidth() const    { return nativePixmap.width;  }
+    virtual     EGLint      getHeight() const   { return nativePixmap.height; }
+    virtual     void*       getBits() const     { return nativePixmap.data; }
+private:
+    egl_native_pixmap_t     nativePixmap;
+};
+
+egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy,
+        EGLConfig config,
+        int32_t depthFormat,
+        egl_native_pixmap_t const * pixmap)
+    : egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap)
+{
+    if (depthFormat) {
+        depth.width   = pixmap->width;
+        depth.height  = pixmap->height;
+        depth.stride  = depth.width; // use the width here
+        depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+        if (depth.data == 0) {
+            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+            return;
+        }
+    }
+}
+EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl)
+{
+    GGLSurface buffer;
+    buffer.version = sizeof(GGLSurface);
+    buffer.width   = nativePixmap.width;
+    buffer.height  = nativePixmap.height;
+    buffer.stride  = nativePixmap.stride;
+    buffer.data    = nativePixmap.data;
+    buffer.format  = nativePixmap.format;
+    
+    gl->rasterizer.procs.colorBuffer(gl, &buffer);
+    if (depth.data != gl->rasterizer.state.buffers.depth.data)
+        gl->rasterizer.procs.depthBuffer(gl, &depth);
+    return EGL_TRUE;
+}
+EGLBoolean egl_pixmap_surface_t::bindReadSurface(ogles_context_t* gl)
+{
+    GGLSurface buffer;
+    buffer.version = sizeof(GGLSurface);
+    buffer.width   = nativePixmap.width;
+    buffer.height  = nativePixmap.height;
+    buffer.stride  = nativePixmap.stride;
+    buffer.data    = nativePixmap.data;
+    buffer.format  = nativePixmap.format;
+    gl->rasterizer.procs.readBuffer(gl, &buffer);
+    return EGL_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+struct egl_pbuffer_surface_t : public egl_surface_t
+{
+    egl_pbuffer_surface_t(
+            EGLDisplay dpy, EGLConfig config, int32_t depthFormat,
+            int32_t w, int32_t h, int32_t f);
+
+    virtual ~egl_pbuffer_surface_t();
+
+    virtual     bool        isValid() const { return pbuffer.data != 0; }    
+    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
+    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
+    virtual     EGLint      getWidth() const    { return pbuffer.width;  }
+    virtual     EGLint      getHeight() const   { return pbuffer.height; }
+    virtual     void*       getBits() const     { return pbuffer.data; }
+private:
+    GGLSurface  pbuffer;
+};
+
+egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy,
+        EGLConfig config, int32_t depthFormat,
+        int32_t w, int32_t h, int32_t f)
+    : egl_surface_t(dpy, config, depthFormat)
+{
+    size_t size = w*h;
+    switch (f) {
+    case GGL_PIXEL_FORMAT_RGB_565:      size *= 2; break;
+    case GGL_PIXEL_FORMAT_RGBA_8888:    size *= 4; break;
+    default:
+        LOGE("incompatible pixel format for pbuffer (format=%d)", f);
+        pbuffer.data = 0;
+        break;
+    }
+    pbuffer.version = sizeof(GGLSurface);
+    pbuffer.width   = w;
+    pbuffer.height  = h;
+    pbuffer.stride  = w;
+    pbuffer.data    = (GGLubyte*)malloc(size);
+    pbuffer.format  = f;
+    
+    if (depthFormat) {
+        depth.width   = pbuffer.width;
+        depth.height  = pbuffer.height;
+        depth.stride  = depth.width; // use the width here
+        depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+        if (depth.data == 0) {
+            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+            return;
+        }
+    }
+}
+egl_pbuffer_surface_t::~egl_pbuffer_surface_t() {
+    free(pbuffer.data);
+}
+EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(ogles_context_t* gl)
+{
+    gl->rasterizer.procs.colorBuffer(gl, &pbuffer);
+    if (depth.data != gl->rasterizer.state.buffers.depth.data)
+        gl->rasterizer.procs.depthBuffer(gl, &depth);
+    return EGL_TRUE;
+}
+EGLBoolean egl_pbuffer_surface_t::bindReadSurface(ogles_context_t* gl)
+{
+    gl->rasterizer.procs.readBuffer(gl, &pbuffer);
+    return EGL_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+struct config_pair_t {
+    GLint key;
+    GLint value;
+};
+
+struct configs_t {
+    const config_pair_t* array;
+    int                  size;
+};
+
+struct config_management_t {
+    GLint key;
+    bool (*match)(GLint reqValue, GLint confValue);
+    static bool atLeast(GLint reqValue, GLint confValue) {
+        return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue);
+    }
+    static bool exact(GLint reqValue, GLint confValue) {
+        return (reqValue == EGL_DONT_CARE) || (confValue == reqValue);
+    }
+    static bool mask(GLint reqValue, GLint confValue) {
+        return (confValue & reqValue) == reqValue;
+    }
+};
+
+// ----------------------------------------------------------------------------
+
+static char const * const gVendorString     = "Google Inc.";
+static char const * const gVersionString    = "1.2 Android Driver";
+static char const * const gClientApiString  = "OpenGL ES";
+static char const * const gExtensionsString =
+    "EGL_ANDROID_swap_rectangle"                " "
+    "EGL_ANDROID_copy_front_to_back"            " "
+    "EGL_ANDROID_get_render_buffer_address"
+    ;
+
+// ----------------------------------------------------------------------------
+
+struct extention_map_t {
+    const char * const name;
+    void (*address)(void);
+};
+
+static const extention_map_t gExtentionMap[] = {
+    { "eglSwapRectangleANDROID",            (void(*)())&eglSwapRectangleANDROID },
+    { "glDrawTexsOES",                      (void(*)())&glDrawTexsOES },
+    { "glDrawTexiOES",                      (void(*)())&glDrawTexiOES },
+    { "glDrawTexfOES",                      (void(*)())&glDrawTexfOES },
+    { "glDrawTexxOES",                      (void(*)())&glDrawTexxOES },
+    { "glDrawTexsvOES",                     (void(*)())&glDrawTexsvOES },
+    { "glDrawTexivOES",                     (void(*)())&glDrawTexivOES },
+    { "glDrawTexfvOES",                     (void(*)())&glDrawTexfvOES },
+    { "glDrawTexxvOES",                     (void(*)())&glDrawTexxvOES },
+    { "glQueryMatrixxOES",                  (void(*)())&glQueryMatrixxOES },
+    { "glClipPlanef",                       (void(*)())&glClipPlanef },
+    { "glClipPlanex",                       (void(*)())&glClipPlanex },
+    { "glBindBuffer",                       (void(*)())&glBindBuffer },
+    { "glBufferData",                       (void(*)())&glBufferData },
+    { "glBufferSubData",                    (void(*)())&glBufferSubData },
+    { "glDeleteBuffers",                    (void(*)())&glDeleteBuffers },
+    { "glGenBuffers",                       (void(*)())&glGenBuffers },
+};
+
+/* 
+ * In the lists below, attributes names MUST be sorted.
+ * Additinnaly, all configs must be sorted according to
+ * the EGL specification.
+ */
+
+static config_pair_t const config_base_attribute_list[] = {
+        { EGL_STENCIL_SIZE,               0                                 },
+        { EGL_CONFIG_CAVEAT,              EGL_SLOW_CONFIG                   },
+        { EGL_LEVEL,                      0                                 },
+        { EGL_MAX_PBUFFER_HEIGHT,         0                                 },
+        { EGL_MAX_PBUFFER_PIXELS,         0                                 },
+        { EGL_MAX_PBUFFER_WIDTH,          0                                 },
+        { EGL_NATIVE_RENDERABLE,          EGL_TRUE                          },
+        { EGL_NATIVE_VISUAL_ID,           0                                 },
+        { EGL_NATIVE_VISUAL_TYPE,         GGL_PIXEL_FORMAT_RGB_565          },
+        { EGL_SAMPLES,                    0                                 },
+        { EGL_SAMPLE_BUFFERS,             0                                 },
+        { EGL_TRANSPARENT_TYPE,           EGL_NONE                          },
+        { EGL_TRANSPARENT_BLUE_VALUE,     0                                 },
+        { EGL_TRANSPARENT_GREEN_VALUE,    0                                 },
+        { EGL_TRANSPARENT_RED_VALUE,      0                                 },
+        { EGL_BIND_TO_TEXTURE_RGBA,       EGL_FALSE                         },
+        { EGL_BIND_TO_TEXTURE_RGB,        EGL_FALSE                         },
+        { EGL_MIN_SWAP_INTERVAL,          1                                 },
+        { EGL_MAX_SWAP_INTERVAL,          4                                 },
+};
+
+// These configs can override the base attribute list
+
+static config_pair_t const config_0_attribute_list[] = {
+        { EGL_BUFFER_SIZE,        16 },
+        { EGL_ALPHA_SIZE,          0 },
+        { EGL_BLUE_SIZE,           5 },
+        { EGL_GREEN_SIZE,          6 },
+        { EGL_RED_SIZE,            5 },
+        { EGL_DEPTH_SIZE,          0 },
+        { EGL_CONFIG_ID,           0 },
+        { EGL_SURFACE_TYPE,        EGL_WINDOW_BIT | EGL_PIXMAP_BIT },
+};
+
+static config_pair_t const config_1_attribute_list[] = {
+        { EGL_BUFFER_SIZE,        16 },
+        { EGL_ALPHA_SIZE,          0 },
+        { EGL_BLUE_SIZE,           5 },
+        { EGL_GREEN_SIZE,          6 },
+        { EGL_RED_SIZE,            5 },
+        { EGL_DEPTH_SIZE,         16 },
+        { EGL_CONFIG_ID,           1 },
+        { EGL_SURFACE_TYPE,        EGL_WINDOW_BIT | EGL_PIXMAP_BIT },
+};
+
+static config_pair_t const config_2_attribute_list[] = {
+        { EGL_BUFFER_SIZE,        32 },
+        { EGL_ALPHA_SIZE,          8 },
+        { EGL_BLUE_SIZE,           8 },
+        { EGL_GREEN_SIZE,          8 },
+        { EGL_RED_SIZE,            8 },
+        { EGL_DEPTH_SIZE,          0 },
+        { EGL_CONFIG_ID,           2 },
+        { EGL_SURFACE_TYPE,        EGL_WINDOW_BIT | EGL_PIXMAP_BIT },
+};
+
+static config_pair_t const config_3_attribute_list[] = {
+        { EGL_BUFFER_SIZE,        32 },
+        { EGL_ALPHA_SIZE,          8 },
+        { EGL_BLUE_SIZE,           8 },
+        { EGL_GREEN_SIZE,          8 },
+        { EGL_RED_SIZE,            8 },
+        { EGL_DEPTH_SIZE,         16 },
+        { EGL_CONFIG_ID,           3 },
+        { EGL_SURFACE_TYPE,        EGL_WINDOW_BIT | EGL_PIXMAP_BIT },
+};
+
+static configs_t const gConfigs[] = {
+        { config_0_attribute_list, NELEM(config_0_attribute_list) },
+        { config_1_attribute_list, NELEM(config_1_attribute_list) },
+        { config_2_attribute_list, NELEM(config_2_attribute_list) },
+        { config_3_attribute_list, NELEM(config_3_attribute_list) },
+};
+
+static config_management_t const gConfigManagement[] = {
+        { EGL_BUFFER_SIZE,                config_management_t::atLeast },
+        { EGL_ALPHA_SIZE,                 config_management_t::atLeast },
+        { EGL_BLUE_SIZE,                  config_management_t::atLeast },
+        { EGL_GREEN_SIZE,                 config_management_t::atLeast },
+        { EGL_RED_SIZE,                   config_management_t::atLeast },
+        { EGL_DEPTH_SIZE,                 config_management_t::atLeast },
+        { EGL_STENCIL_SIZE,               config_management_t::atLeast },
+        { EGL_CONFIG_CAVEAT,              config_management_t::exact   },
+        { EGL_CONFIG_ID,                  config_management_t::exact   },
+        { EGL_LEVEL,                      config_management_t::exact   },
+        { EGL_MAX_PBUFFER_HEIGHT,         config_management_t::exact   },
+        { EGL_MAX_PBUFFER_PIXELS,         config_management_t::exact   },
+        { EGL_MAX_PBUFFER_WIDTH,          config_management_t::exact   },
+        { EGL_NATIVE_RENDERABLE,          config_management_t::exact   },
+        { EGL_NATIVE_VISUAL_ID,           config_management_t::exact   },
+        { EGL_NATIVE_VISUAL_TYPE,         config_management_t::exact   },
+        { EGL_SAMPLES,                    config_management_t::exact   },
+        { EGL_SAMPLE_BUFFERS,             config_management_t::exact   },
+        { EGL_SURFACE_TYPE,               config_management_t::mask    },
+        { EGL_TRANSPARENT_TYPE,           config_management_t::exact   },
+        { EGL_TRANSPARENT_BLUE_VALUE,     config_management_t::exact   },
+        { EGL_TRANSPARENT_GREEN_VALUE,    config_management_t::exact   },
+        { EGL_TRANSPARENT_RED_VALUE,      config_management_t::exact   },
+        { EGL_BIND_TO_TEXTURE_RGBA,       config_management_t::exact   },
+        { EGL_BIND_TO_TEXTURE_RGB,        config_management_t::exact   },
+        { EGL_MIN_SWAP_INTERVAL,          config_management_t::exact   },
+        { EGL_MAX_SWAP_INTERVAL,          config_management_t::exact   },
+};
+
+static config_pair_t const config_defaults[] = {
+        { EGL_SURFACE_TYPE,        EGL_WINDOW_BIT },
+};
+
+// ----------------------------------------------------------------------------
+
+template<typename T>
+static int binarySearch(T const sortedArray[], int first, int last, EGLint key)
+{
+   while (first <= last) {
+       int mid = (first + last) / 2;
+       if (key > sortedArray[mid].key) { 
+           first = mid + 1;
+       } else if (key < sortedArray[mid].key) { 
+           last = mid - 1;
+       } else {
+           return mid;
+       }
+   }
+   return -1;
+}
+
+static int isAttributeMatching(int i, EGLint attr, EGLint val)
+{
+    // look for the attribute in all of our configs
+    config_pair_t const* configFound = gConfigs[i].array; 
+    int index = binarySearch<config_pair_t>(
+            gConfigs[i].array,
+            0, gConfigs[i].size-1,
+            attr);
+    if (index < 0) {
+        configFound = config_base_attribute_list; 
+        index = binarySearch<config_pair_t>(
+                config_base_attribute_list,
+                0, NELEM(config_base_attribute_list)-1,
+                attr);
+    }
+    if (index >= 0) {
+        // attribute found, check if this config could match
+        int cfgMgtIndex = binarySearch<config_management_t>(
+                gConfigManagement,
+                0, NELEM(gConfigManagement)-1,
+                attr);
+        if (index >= 0) {
+            bool match = gConfigManagement[cfgMgtIndex].match(
+                    val, configFound[index].value);
+            if (match) {
+                // this config matches
+                return 1;
+            }
+        } else {
+            // attribute nont found. this should NEVER happen.
+        }
+    } else {
+        // error, this attribute doesn't exist
+    }
+    return 0;
+}
+
+static int makeCurrent(ogles_context_t* gl)
+{
+    ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
+    if (gl) {
+        egl_context_t* c = egl_context_t::context(gl);
+        if (c->flags & egl_context_t::IS_CURRENT) {
+            if (current != gl) {
+                // it is an error to set a context current, if it's already
+                // current to another thread
+                return -1;
+            }
+        } else {
+            if (current) {
+                // mark the current context as not current, and flush
+                glFlush();
+                egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
+            }
+        }
+        if (!(c->flags & egl_context_t::IS_CURRENT)) {
+            // The context is not current, make it current!
+            setGlThreadSpecific(gl);
+            c->flags |= egl_context_t::IS_CURRENT;
+        }
+    } else {
+        if (current) {
+            // mark the current context as not current, and flush
+            glFlush();
+            egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
+        }
+        // this thread has no context attached to it
+        setGlThreadSpecific(0);
+    }
+    return 0;
+}
+
+static EGLBoolean getConfigAttrib(EGLDisplay dpy, EGLConfig config,
+        EGLint attribute, EGLint *value)
+{
+    size_t numConfigs =  NELEM(gConfigs);
+    int index = (int)config;
+    if (uint32_t(index) >= numConfigs)
+        return setError(EGL_BAD_CONFIG, EGL_FALSE);
+
+    int attrIndex;
+    attrIndex = binarySearch<config_pair_t>(
+            gConfigs[index].array,
+            0, gConfigs[index].size-1,
+            attribute);
+    if (attrIndex>=0) {
+        *value = gConfigs[index].array[attrIndex].value;
+        return EGL_TRUE;
+    }
+
+    attrIndex = binarySearch<config_pair_t>(
+            config_base_attribute_list,
+            0, NELEM(config_base_attribute_list)-1,
+            attribute);
+    if (attrIndex>=0) {
+        *value = config_base_attribute_list[attrIndex].value;
+        return EGL_TRUE;
+    }
+    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+}
+
+static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
+        NativeWindowType window, const EGLint *attrib_list)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+    if (window == 0)
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    EGLint surfaceType;
+    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
+        return EGL_FALSE;
+
+    if (!(surfaceType & EGL_WINDOW_BIT))
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    EGLint configID;
+    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
+        return EGL_FALSE;
+
+    int32_t depthFormat;
+    int32_t pixelFormat;
+    switch(configID) {
+    case 0: 
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = 0;
+        break;
+    case 1:
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    case 2:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = 0;
+        break;
+    case 3:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    default:
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+    }
+
+    // XXX: we don't have access to the pixelFormat here just yet.
+    // (it's possible that the surface is not fully initialized)
+    // maybe this should be done after the page-flip
+    //if (EGLint(info.format) != pixelFormat)
+    //    return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    egl_surface_t* surface =
+        new egl_window_surface_t(dpy, config, depthFormat,
+                static_cast<egl_native_window_t*>(window));
+
+    if (!surface->isValid()) {
+        // there was a problem in the ctor, the error
+        // flag has been set.
+        delete surface;
+        surface = 0;
+    }
+    return surface;
+}
+
+static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config,
+        NativePixmapType pixmap, const EGLint *attrib_list)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+    if (pixmap == 0)
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    EGLint surfaceType;
+    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
+        return EGL_FALSE;
+
+    if (!(surfaceType & EGL_PIXMAP_BIT))
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    EGLint configID;
+    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
+        return EGL_FALSE;
+
+    int32_t depthFormat;
+    int32_t pixelFormat;
+    switch(configID) {
+    case 0: 
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = 0;
+        break;
+    case 1:
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    case 2:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = 0;
+        break;
+    case 3:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    default:
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+    }
+
+    if (pixmap->format != pixelFormat)
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+
+    egl_surface_t* surface =
+        new egl_pixmap_surface_t(dpy, config, depthFormat,
+                static_cast<egl_native_pixmap_t*>(pixmap));
+
+    if (!surface->isValid()) {
+        // there was a problem in the ctor, the error
+        // flag has been set.
+        delete surface;
+        surface = 0;
+    }
+    return surface;
+}
+
+static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config,
+        const EGLint *attrib_list)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+
+    EGLint surfaceType;
+    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
+        return EGL_FALSE;
+    
+    if (!(surfaceType & EGL_PBUFFER_BIT))
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+        
+    EGLint configID;
+    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
+        return EGL_FALSE;
+
+    int32_t depthFormat;
+    int32_t pixelFormat;
+    switch(configID) {
+    case 0: 
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = 0;
+        break;
+    case 1:
+        pixelFormat = GGL_PIXEL_FORMAT_RGB_565; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    case 2:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = 0;
+        break;
+    case 3:
+        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; 
+        depthFormat = GGL_PIXEL_FORMAT_Z_16;
+        break;
+    default:
+        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+    }
+
+    int32_t w = 0;
+    int32_t h = 0;
+    while (attrib_list[0]) {
+        if (attrib_list[0] == EGL_WIDTH)  w = attrib_list[1];
+        if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1];
+        attrib_list+=2;
+    }
+
+    egl_surface_t* surface =
+        new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat);
+
+    if (!surface->isValid()) {
+        // there was a problem in the ctor, the error
+        // flag has been set.
+        delete surface;
+        surface = 0;
+    }
+    return surface;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLDisplay eglGetDisplay(NativeDisplayType display)
+{
+#ifndef HAVE_ANDROID_OS
+    // this just needs to be done once
+    if (gGLKey == -1) {
+        pthread_mutex_lock(&gInitMutex);
+        if (gGLKey == -1)
+            pthread_key_create(&gGLKey, NULL);
+        pthread_mutex_unlock(&gInitMutex);
+    }
+#endif
+    if (display == EGL_DEFAULT_DISPLAY) {
+        EGLDisplay dpy = (EGLDisplay)1;
+        egl_display_t& d = egl_display_t::get_display(dpy);
+        d.type = display;
+        return dpy;
+    }    
+    return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    
+    EGLBoolean res = EGL_TRUE;
+    egl_display_t& d = egl_display_t::get_display(dpy);
+    
+    if (android_atomic_inc(&d.initialized) == 0) {
+        // initialize stuff here if needed
+        //pthread_mutex_lock(&gInitMutex);
+        //pthread_mutex_unlock(&gInitMutex);
+    }
+
+    if (res == EGL_TRUE) {
+        if (major != NULL) *major = 1;
+        if (minor != NULL) *minor = 2;
+    }
+    return res;
+}
+
+EGLBoolean eglTerminate(EGLDisplay dpy)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    EGLBoolean res = EGL_TRUE;
+    egl_display_t& d = egl_display_t::get_display(dpy);
+    if (android_atomic_dec(&d.initialized) == 1) {
+        // TODO: destroy all resources (surfaces, contextes, etc...)
+        //pthread_mutex_lock(&gInitMutex);
+        //pthread_mutex_unlock(&gInitMutex);
+    }
+    return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigs(   EGLDisplay dpy,
+                            EGLConfig *configs,
+                            EGLint config_size, EGLint *num_config)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    GLint numConfigs = NELEM(gConfigs);
+    if (!configs) {
+        *num_config = numConfigs;
+        return EGL_TRUE;
+    }
+    GLint i;
+    for (i=0 ; i<numConfigs && i<config_size ; i++) {
+        *configs++ = (EGLConfig)i;
+    }
+    *num_config = i;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
+                            EGLConfig *configs, EGLint config_size,
+                            EGLint *num_config)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    if (ggl_unlikely(configs==0 || attrib_list==0)) {
+        *num_config = 0;
+        return EGL_TRUE;
+    }
+    
+    int numAttributes = 0;
+    int numConfigs =  NELEM(gConfigs);
+    uint32_t possibleMatch = (1<<numConfigs)-1;
+    while(possibleMatch && *attrib_list != EGL_NONE) {
+        numAttributes++;
+        EGLint attr = *attrib_list++;
+        EGLint val  = *attrib_list++;
+        for (int i=0 ; i<numConfigs ; i++) {
+            if (!(possibleMatch & (1<<i)))
+                continue;
+            if (isAttributeMatching(i, attr, val) == 0) {
+                possibleMatch &= ~(1<<i);
+            }
+        }
+    }
+
+    // now, handle the attributes which have a useful default value
+    for (size_t j=0 ; j<NELEM(config_defaults) ; j++) {
+        // see if this attribute was specified, if not apply its
+        // default value
+        if (binarySearch<config_pair_t>(
+                (config_pair_t const*)attrib_list,
+                0, numAttributes,
+                config_defaults[j].key) < 0)
+        {
+            for (int i=0 ; i<numConfigs ; i++) {
+                if (!(possibleMatch & (1<<i)))
+                    continue;
+                if (isAttributeMatching(i,
+                        config_defaults[j].key,
+                        config_defaults[j].value) == 0)
+                {
+                    possibleMatch &= ~(1<<i);
+                }
+            }
+        }
+    }
+
+    // return the configurations found
+    int n=0;
+    if (possibleMatch) {
+        for (int i=0 ; config_size && i<numConfigs ; i++) {
+            if (possibleMatch & (1<<i)) {
+               *configs++ = (EGLConfig)i;
+                config_size--;
+                n++;
+            }
+        }
+    }
+    *num_config = n;
+     return EGL_TRUE;
+}
+
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
+        EGLint attribute, EGLint *value)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    return getConfigAttrib(dpy, config, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
+                                    NativeWindowType window,
+                                    const EGLint *attrib_list)
+{
+    return createWindowSurface(dpy, config, window, attrib_list);
+}
+    
+EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
+                                    NativePixmapType pixmap,
+                                    const EGLint *attrib_list)
+{
+    return createPixmapSurface(dpy, config, pixmap, attrib_list);
+}
+
+EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
+                                    const EGLint *attrib_list)
+{
+    // none of our configs support pbuffers
+    // (in fact it's working but since we can't use them as 
+    // textures yet, it's not useful at all)
+    //createPbufferSurface(dpy, config, attrib_list);
+    return EGL_NO_SURFACE;
+}
+                                    
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (eglSurface != EGL_NO_SURFACE) {
+        egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
+        if (surface->magic != egl_surface_t::MAGIC)
+            return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (surface->dpy != dpy)
+            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        delete surface;
+    }
+    return EGL_TRUE;
+}
+
+EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface,
+                            EGLint attribute, EGLint *value)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface);
+    if (surface->dpy != dpy)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    EGLBoolean ret = EGL_TRUE;
+    switch (attribute) {
+        case EGL_CONFIG_ID:
+            ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value);
+            break;
+        case EGL_WIDTH:
+            *value = surface->getWidth();
+            break;
+        case EGL_HEIGHT:
+            *value = surface->getHeight();
+            break;
+        case EGL_LARGEST_PBUFFER:
+            // not modified for a window or pixmap surface
+            break;
+        case EGL_TEXTURE_FORMAT:
+            *value = EGL_NO_TEXTURE;
+            break;
+        case EGL_TEXTURE_TARGET:
+            *value = EGL_NO_TEXTURE;
+            break;
+        case EGL_MIPMAP_TEXTURE:
+            *value = EGL_FALSE;
+            break;
+        case EGL_MIPMAP_LEVEL:
+            *value = 0;
+            break;
+        case EGL_RENDER_BUFFER:
+            // TODO: return the real RENDER_BUFFER here
+            *value = EGL_BACK_BUFFER;
+            break;
+        case EGL_HORIZONTAL_RESOLUTION:
+            // pixel/mm * EGL_DISPLAY_SCALING
+            *value = surface->getHorizontalResolution();
+            break;
+        case EGL_VERTICAL_RESOLUTION:
+            // pixel/mm * EGL_DISPLAY_SCALING
+            *value = surface->getVerticalResolution();
+            break;
+        case EGL_PIXEL_ASPECT_RATIO: {
+            // w/h * EGL_DISPLAY_SCALING
+            int wr = surface->getHorizontalResolution();
+            int hr = surface->getVerticalResolution();
+            *value = (wr * EGL_DISPLAY_SCALING) / hr;
+        } break;
+        case EGL_SWAP_BEHAVIOR:
+            *value = surface->getSwapBehavior(); 
+            break;
+        default:
+            ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+    }
+    return ret;
+}
+
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
+                            EGLContext share_list, const EGLint *attrib_list)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+
+    ogles_context_t* gl = ogles_init(sizeof(egl_context_t));
+    if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
+
+    egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base);
+    c->flags = egl_context_t::NEVER_CURRENT;
+    c->dpy = dpy;
+    c->config = config;
+    c->read = 0;
+    c->draw = 0;
+    return (EGLContext)gl;
+}
+
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    egl_context_t* c = egl_context_t::context(ctx);
+    if (c->flags & egl_context_t::IS_CURRENT)
+        setGlThreadSpecific(0);
+    ogles_uninit((ogles_context_t*)ctx);
+    return EGL_TRUE;
+}
+
+EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
+                            EGLSurface read, EGLContext ctx)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (draw) {
+        egl_surface_t* s = (egl_surface_t*)draw;
+        if (s->dpy != dpy)
+            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        // TODO: check that draw and read are compatible with the context
+    }
+
+    EGLContext current_ctx = EGL_NO_CONTEXT;
+    if (ctx == EGL_NO_CONTEXT) {
+        // if we're detaching, we need the current context
+        current_ctx = (EGLContext)getGlThreadSpecific();
+    } else {
+        egl_context_t* c = egl_context_t::context(ctx);
+        egl_surface_t* d = (egl_surface_t*)draw;
+        egl_surface_t* r = (egl_surface_t*)read;
+        if ((d && d->ctx && d->ctx != ctx) ||
+            (r && r->ctx && r->ctx != ctx)) {
+            // once of the surface is bound to a context in another thread
+            return setError(EGL_BAD_ACCESS, EGL_FALSE);
+        }
+    }
+
+    ogles_context_t* gl = (ogles_context_t*)ctx;
+    if (makeCurrent(gl) == 0) {
+        if (ctx) {
+            egl_context_t* c = egl_context_t::context(ctx);
+            egl_surface_t* d = (egl_surface_t*)draw;
+            egl_surface_t* r = (egl_surface_t*)read;
+            c->read = read;
+            c->draw = draw;
+            if (c->flags & egl_context_t::NEVER_CURRENT) {
+                c->flags &= ~egl_context_t::NEVER_CURRENT;
+                GLint w = 0;
+                GLint h = 0;
+                if (draw) {
+                    w = d->getWidth();
+                    h = d->getHeight();
+                }
+                ogles_surfaceport(gl, 0, 0);
+                ogles_viewport(gl, 0, 0, w, h);
+                ogles_scissor(gl, 0, 0, w, h);
+            }
+            if (d) {
+                d->ctx = ctx;
+                d->bindDrawSurface(gl);
+            }
+            if (r) {
+                r->ctx = ctx;
+                r->bindReadSurface(gl);
+            }
+        } else {
+            // if surfaces were bound to the context bound to this thread
+            // mark then as unbound.
+            if (current_ctx) {
+                egl_context_t* c = egl_context_t::context(current_ctx);
+                egl_surface_t* d = (egl_surface_t*)c->draw;
+                egl_surface_t* r = (egl_surface_t*)c->read;
+                if (d) d->ctx = EGL_NO_CONTEXT;
+                if (r) r->ctx = EGL_NO_CONTEXT;
+            }
+        }
+        return EGL_TRUE;
+    }
+    return setError(EGL_BAD_ACCESS, EGL_FALSE);
+}
+
+EGLContext eglGetCurrentContext(void)
+{
+    // eglGetCurrentContext returns the current EGL rendering context,
+    // as specified by eglMakeCurrent. If no context is current,
+    // EGL_NO_CONTEXT is returned.
+    return (EGLContext)getGlThreadSpecific();
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw)
+{
+    // eglGetCurrentSurface returns the read or draw surface attached
+    // to the current EGL rendering context, as specified by eglMakeCurrent.
+    // If no context is current, EGL_NO_SURFACE is returned.
+    EGLContext ctx = (EGLContext)getGlThreadSpecific();
+    if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE;
+    egl_context_t* c = egl_context_t::context(ctx);
+    if (readdraw == EGL_READ) {
+        return c->read;
+    } else if (readdraw == EGL_DRAW) {
+        return c->draw;
+    }
+    return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+}
+
+EGLDisplay eglGetCurrentDisplay(void)
+{
+    // eglGetCurrentDisplay returns the current EGL display connection
+    // for the current EGL rendering context, as specified by eglMakeCurrent.
+    // If no context is current, EGL_NO_DISPLAY is returned.
+    EGLContext ctx = (EGLContext)getGlThreadSpecific();
+    if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY;
+    egl_context_t* c = egl_context_t::context(ctx);
+    return c->dpy;
+}
+
+EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
+                            EGLint attribute, EGLint *value)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    egl_context_t* c = egl_context_t::context(ctx);
+    switch (attribute) {
+        case EGL_CONFIG_ID:
+            // Returns the ID of the EGL frame buffer configuration with
+            // respect to which the context was created
+            return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value);
+    }
+    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+}
+
+EGLBoolean eglWaitGL(void)
+{
+    return EGL_TRUE;
+}
+
+EGLBoolean eglWaitNative(EGLint engine)
+{
+    return EGL_TRUE;
+}
+
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    
+    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+    if (d->dpy != dpy)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    // post the surface
+    d->swapBuffers();
+
+    // if it's bound to a context, update the buffer
+    if (d->ctx != EGL_NO_CONTEXT) {
+        d->bindDrawSurface((ogles_context_t*)d->ctx);
+        // if this surface is also the read surface of the context
+        // it is bound to, make sure to update the read buffer as well.
+        // The EGL spec is a little unclear about this.
+        egl_context_t* c = egl_context_t::context(d->ctx);
+        if (c->read == draw) {
+            d->bindReadSurface((ogles_context_t*)d->ctx);
+        }
+    }
+
+    return EGL_TRUE;
+}
+
+EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
+                            NativePixmapType target)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    // TODO: eglCopyBuffers()
+    return EGL_FALSE;
+}
+
+EGLint eglGetError(void)
+{
+    return getError();
+}
+
+const char* eglQueryString(EGLDisplay dpy, EGLint name)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, (const char*)0);
+
+    switch (name) {
+        case EGL_VENDOR:
+            return gVendorString;
+        case EGL_VERSION:
+            return gVersionString;
+        case EGL_EXTENSIONS:
+            return gExtensionsString;
+        case EGL_CLIENT_APIS:
+            return gClientApiString;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)0);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttrib(
+        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    // TODO: eglSurfaceAttrib()
+    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImage(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    // TODO: eglBindTexImage()
+    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImage(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    // TODO: eglReleaseTexImage()
+    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    // TODO: eglSwapInterval()
+    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglBindAPI(EGLenum api)
+{
+    if (api != EGL_OPENGL_ES_API)
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    return EGL_TRUE;
+}
+
+EGLenum eglQueryAPI(void)
+{
+    return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglWaitClient(void)
+{
+    glFinish();
+    return EGL_TRUE;
+}
+
+EGLBoolean eglReleaseThread(void)
+{
+    // TODO: eglReleaseThread()
+    return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBuffer(
+          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+          EGLConfig config, const EGLint *attrib_list)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+    // TODO: eglCreatePbufferFromClientBuffer()
+    return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// Android extentions
+// ----------------------------------------------------------------------------
+
+void (*eglGetProcAddress (const char *procname))()
+{
+    extention_map_t const * const map = gExtentionMap;
+    for (uint32_t i=0 ; i<NELEM(gExtentionMap) ; i++) {
+        if (!strcmp(procname, map[i].name)) {
+            return map[i].address;
+        }
+    }
+    return NULL;
+}
+
+EGLBoolean eglSwapRectangleANDROID(
+        EGLDisplay dpy, EGLSurface draw,
+        EGLint l, EGLint t, EGLint w, EGLint h)
+{    
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    egl_surface_t* surface = (egl_surface_t*)draw;
+    if (surface->dpy != dpy)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    return surface->swapRectangle(l, t, w, h);
+}
diff --git a/opengl/libagl/fixed_asm.S b/opengl/libagl/fixed_asm.S
new file mode 100644
index 0000000..6cbc56f
--- /dev/null
+++ b/opengl/libagl/fixed_asm.S
@@ -0,0 +1,65 @@
+/* libs/opengles/fixed_asm.S
+**
+** Copyright 2006, 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.
+*/
+
+
+    .text
+    .align
+    
+    .global gglFloatToFixed
+    .global gglFloatToFixedFast
+
+
+/*
+ * Converts a float to a s15.16 fixed-point number.
+ * this doesn't handle floats out of the [-32768, +32768[ range
+ * and doesn't performs round-to-nearest.
+ * however, it's very fast :-)
+ */
+
+gglFloatToFixedFast:
+        movs    r1, r0, lsl #1          /* remove bit sign */
+        mov     r2, #0x8E               /* 127 + 15 */
+        sub     r1, r2, r1, lsr #24     /* compute shift */
+        mov     r2, r0, lsl #8          /* mantissa<<8 */
+        orr     r2, r2, #0x80000000     /* add the missing 1 */
+        mov     r0, r2, lsr r1          /* scale to 16.16 */
+        rsbcs   r0, r0, #0              /* negate if needed */
+        bx      lr
+
+/*
+ * this version rounds-to-nearest and saturates numbers
+ * outside the range (but not NaNs).
+ */
+
+gglFloatToFixed:
+        mov     r1, r0, lsl #1          /* remove bit sign */
+        mov     r2, #0x8E               /* 127 + 15 */
+        subs    r1, r2, r1, lsr #24     /* compute shift */
+        bls     0f                      /* too big */
+        mov     r2, r0, lsl #8          /* mantissa<<8 */
+        orr     r2, r2, #0x80000000     /* add the missing 1 */
+        mov     r3, r0
+        movs    r0, r2, lsr r1          /* scale to 16.16 */
+        addcs   r0, r0, #1              /* round-to-nearest */
+        tst     r3, #0x80000000         /* negative? */
+        rsbne   r0, r0, #0              /* negate if needed */
+        bx      lr 
+ 
+0:      ands    r0, r0, #0x80000000     /* keep only the sign bit */
+        moveq   r0, #0x7fffffff         /* positive, maximum value */
+        bx      lr
+
diff --git a/opengl/libagl/fp.cpp b/opengl/libagl/fp.cpp
new file mode 100644
index 0000000..ae5f1fe
--- /dev/null
+++ b/opengl/libagl/fp.cpp
@@ -0,0 +1,87 @@
+/* libs/opengles/fp.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include "fp.h"
+
+// ----------------------------------------------------------------------------
+
+#if !defined(__arm__)
+GGLfixed gglFloatToFixed(float v) {   
+    return GGLfixed(floorf(v * 65536.0f + 0.5f));
+}
+#endif
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+namespace gl {
+
+GLfloat fixedToFloat(GLfixed x)
+{
+#if DEBUG_USE_FLOATS
+    return x / 65536.0f;
+#else
+    if (!x) return 0;
+    const uint32_t s = x & 0x80000000;
+    union {
+        uint32_t i;
+        float f;
+    };
+    i = s ? -x : x;
+    const int c = gglClz(i) - 8;
+    i = (c>=0) ? (i<<c) : (i>>-c);
+    const uint32_t e = 134 - c;
+    i &= ~0x800000;
+    i |= e<<23;
+    i |= s;
+    return f;
+#endif
+}
+
+float sinef(float x)
+{
+    const float A =   1.0f / (2.0f*M_PI);
+    const float B = -16.0f;
+    const float C =   8.0f;
+
+    // scale angle for easy argument reduction
+    x *= A;
+    
+    if (fabsf(x) >= 0.5f) {
+        // Argument reduction
+        x = x - ceilf(x + 0.5f) + 1.0f; 
+    }
+
+    const float y = B*x*fabsf(x) + C*x;
+    return 0.2215f * (y*fabsf(y) - y) + y;
+}
+
+float cosinef(float x)
+{
+    return sinef(x + float(M_PI/2));
+}
+
+void sincosf(GLfloat angle, GLfloat* s, GLfloat* c) {
+    *s = sinef(angle);
+    *c = cosinef(angle);
+}
+
+}; // namespace fp_utils
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/opengl/libagl/fp.h b/opengl/libagl/fp.h
new file mode 100644
index 0000000..6d0c183
--- /dev/null
+++ b/opengl/libagl/fp.h
@@ -0,0 +1,243 @@
+/* libs/opengles/fp.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_FP_H
+#define ANDROID_OPENGLES_FP_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include <GLES/gl.h>
+
+#define DEBUG_USE_FLOATS      0
+
+// ----------------------------------------------------------------------------
+
+extern "C" GLfixed gglFloatToFixed(float f) __attribute__((const));
+
+// ----------------------------------------------------------------------------
+namespace android {
+
+namespace gl {
+
+        GLfloat fixedToFloat(GLfixed) CONST;
+
+        void    sincosf(GLfloat angle, GLfloat* s, GLfloat* c);
+        float   sinef(GLfloat x) CONST;
+        float   cosinef(GLfloat x) CONST;
+
+inline bool     cmpf(GLfloat a, GLfloat b) CONST;
+inline bool     isZerof(GLfloat) CONST;
+inline bool     isOnef(GLfloat) CONST;
+
+inline int      isZeroOrNegativef(GLfloat) CONST;
+
+inline int      exponent(GLfloat) CONST;
+inline int32_t  mantissa(GLfloat) CONST;
+inline GLfloat  clampToZerof(GLfloat) CONST;
+inline GLfloat  reciprocalf(GLfloat) CONST;
+inline GLfloat  rsqrtf(GLfloat) CONST;
+inline GLfloat  sqrf(GLfloat) CONST;
+inline GLfloat  addExpf(GLfloat v, int e) CONST;
+inline GLfloat  mul2f(GLfloat v) CONST;
+inline GLfloat  div2f(GLfloat v) CONST;
+inline GLfloat  absf(GLfloat v) CONST;
+
+
+/* 
+ * float fastexpf(float) : a fast approximation of expf(x)
+ *		give somewhat accurate results for -88 <= x <= 88
+ *
+ * exp(x) = 2^(x/ln(2))
+ * we use the properties of float encoding
+ * to get a fast 2^ and linear interpolation
+ *
+ */
+
+inline float fastexpf(float y) __attribute__((const));
+
+inline float fastexpf(float y)
+{
+	union {
+		float	r;
+		int32_t	i;
+	} u;	
+
+	// 127*ln(2) = 88
+	if (y < -88.0f) {
+		u.r = 0.0f;
+	} else if (y > 88.0f) {
+		u.r = INFINITY;
+	} else {
+		const float kOneOverLogTwo = (1L<<23) / M_LN2;
+		const int32_t kExponentBias = 127L<<23;
+		const int32_t e = int32_t(y*kOneOverLogTwo);
+		u.i = e + kExponentBias;
+	}
+	
+	return u.r;
+}
+
+
+bool cmpf(GLfloat a, GLfloat b) {
+#if DEBUG_USE_FLOATS
+    return a == b;
+#else
+    union {
+        float       f;
+        uint32_t    i;
+    } ua, ub;
+    ua.f = a;
+    ub.f = b;
+    return ua.i == ub.i;
+#endif
+} 
+
+bool isZerof(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v == 0;
+#else
+    union {
+        float       f;
+        int32_t     i;
+    };
+    f = v;
+    return (i<<1) == 0;
+#endif
+}
+
+bool isOnef(GLfloat v) {
+    return cmpf(v, 1.0f);
+}
+
+int isZeroOrNegativef(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v <= 0;
+#else
+    union {
+        float       f;
+        int32_t     i;
+    };
+    f = v;
+    return isZerof(v) | (i>>31);
+#endif
+}
+
+int exponent(GLfloat v) {
+    union {
+        float    f;
+        uint32_t i;
+    };
+    f = v;
+    return ((i << 1) >> 24) - 127;
+}
+
+int32_t mantissa(GLfloat v) {
+    union {
+        float    f;
+        uint32_t i;
+    };
+    f = v;
+    if (!(i&0x7F800000)) return 0;
+    const int s = i >> 31;
+    i |= (1L<<23);
+    i &= ~0xFF000000;
+    return s ? -i : i;
+}
+
+GLfloat clampToZerof(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v<0 ? 0 : (v>1 ? 1 : v);
+#else
+    union {
+        float       f;
+        int32_t     i;
+    };
+    f = v;
+    i &= ~(i>>31);
+    return f;
+#endif
+}
+
+GLfloat reciprocalf(GLfloat v) {
+    // XXX: do better
+    return 1.0f / v;
+}
+
+GLfloat rsqrtf(GLfloat v) {
+    // XXX: do better
+    return 1.0f / sqrtf(v);
+}
+
+GLfloat sqrf(GLfloat v) {
+    // XXX: do better
+    return v*v;
+}
+
+GLfloat addExpf(GLfloat v, int e) {
+    union {
+        float       f;
+        int32_t     i;
+    };
+    f = v;
+    if (i<<1) { // XXX: deal with over/underflow	
+        i += int32_t(e)<<23;
+    }
+    return f;
+}
+
+GLfloat mul2f(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v*2;
+#else
+    return addExpf(v, 1);
+#endif
+}
+
+GLfloat div2f(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v*0.5f;
+#else
+    return addExpf(v, -1);
+#endif
+}
+
+GLfloat  absf(GLfloat v) {
+#if DEBUG_USE_FLOATS
+    return v<0 ? -v : v;
+#else
+    union {
+        float       f;
+        int32_t     i;
+    };
+    f = v;
+    i &= ~0x80000000;
+    return f;
+#endif
+}
+
+};  // namespace gl
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_FP_H
+
diff --git a/opengl/libagl/iterators.S b/opengl/libagl/iterators.S
new file mode 100644
index 0000000..daf2937
--- /dev/null
+++ b/opengl/libagl/iterators.S
@@ -0,0 +1,88 @@
+/* libs/opengles/iterators.S
+**
+** Copyright 2006, 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.
+*/
+
+
+    .text
+    .align
+    .arm
+    
+    .global iterators0032
+
+/*
+ * iterators0032
+ *
+ * MUST BE CALLED FROM ARM CODE
+ *
+ * r0: const compute_iterators_t* (this)
+ *      r0 + 0: m_dx01 
+ *      r0 + 4: m_dy10
+ *      r0 + 8: m_dx20
+ *      r0 +12: m_dy02
+ *      r0 +16: m_x0
+ *      r0 +20: m_y0
+ *      r0 +24: m_area
+ *		r0 +28: m_scale
+ *		r0 +29: m_area_scale;
+ * r1: int32_t* (out)
+ *      r1 + 0: c
+ *      r1 + 4: dcdx
+ *      r1 + 8: dcdy
+ *   r2: c0
+ *   r3: c1
+ * [sp]: c2
+ */
+ 
+iterators0032:
+        stmfd	sp!, {r4, r5, r6, r7, r8, lr}
+        ldr     r4, [sp, #4*6]
+
+        ldrb    r12, [r0, #29]
+        sub     r3, r3, r2
+        sub     r4, r4, r2
+        sub     r12, r12, #16
+        mov     r3, r3, asr r12
+        mov     r4, r4, asr r12
+        
+        ldr     r5, [r0, #0]
+        ldr     r12, [r0, #4]
+        smull   r8, lr, r4, r5
+        ldr     r5, [r0, #8]
+        smull   r6, r7, r4, r12
+        ldr     r12, [r0, #12]
+        smlal   r8, lr, r3, r5
+        smlal   r6, r7, r3, r12
+
+        ldr     r3, [r0, #16]        // m_x0
+        ldr     r4, [r0, #20]        // m_x1
+        
+        str     r6, [r1, #4]
+        str     r8, [r1, #8]
+
+        umull   r6, r5, r3, r6
+        umull   r8, r0, r4, r8
+        mla     r7, r3, r7, r5
+        mla     lr, r4, lr, r0
+        adds    r6, r6, r8
+        adc     r7, r7, lr
+
+        movs    r6, r6, lsr #4
+        adc     r6, r6, r7, lsl #28
+        rsb     r6, r6, r2, lsl #16
+        str     r6, [r1, #0]
+
+        ldmfd	sp!, {r4, r5, r6, r7, r8, pc}
+
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
new file mode 100644
index 0000000..87725cb
--- /dev/null
+++ b/opengl/libagl/light.cpp
@@ -0,0 +1,852 @@
+/* libs/opengles/light.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include "context.h"
+#include "fp.h"
+#include "light.h"
+#include "state.h"
+#include "matrix.h"
+
+
+#if defined(__arm__) && defined(__thumb__)
+#warning "light.cpp should not be compiled in thumb on ARM."
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static void invalidate_lighting(ogles_context_t* c);
+static void lightVertexValidate(ogles_context_t* c, vertex_t* v);
+static void lightVertexNop(ogles_context_t* c, vertex_t* v);
+static void lightVertex(ogles_context_t* c, vertex_t* v);
+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 vmla3(GLfixed* d,
+    const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
+static inline void vmul3(GLfixed* d,
+    const GLfixed* m0, const GLfixed* m1);
+
+static GLfixed fog_linear(ogles_context_t* c, GLfixed z);
+static GLfixed fog_exp(ogles_context_t* c, GLfixed z);
+static GLfixed fog_exp2(ogles_context_t* c, GLfixed z);
+
+
+// ----------------------------------------------------------------------------
+
+static void init_white(vec4_t& c) {
+    c.r = c.g = c.b = c.a = 0x10000;
+}
+
+void ogles_init_light(ogles_context_t* c)
+{
+    for (unsigned int i=0 ; i<OGLES_MAX_LIGHTS ; i++) {
+        c->lighting.lights[i].ambient.a = 0x10000;
+        c->lighting.lights[i].position.z = 0x10000;
+        c->lighting.lights[i].spotDir.z = -0x10000;
+        c->lighting.lights[i].spotCutoff = gglIntToFixed(180);
+        c->lighting.lights[i].attenuation[0] = 0x10000;
+    }
+    init_white(c->lighting.lights[0].diffuse);
+    init_white(c->lighting.lights[0].specular);
+
+    c->lighting.front.ambient.r =
+    c->lighting.front.ambient.g =
+    c->lighting.front.ambient.b = gglFloatToFixed(0.2f);
+    c->lighting.front.ambient.a = 0x10000;
+    c->lighting.front.diffuse.r =
+    c->lighting.front.diffuse.g =
+    c->lighting.front.diffuse.b = gglFloatToFixed(0.8f);
+    c->lighting.front.diffuse.a = 0x10000;
+    c->lighting.front.specular.a = 0x10000;
+    c->lighting.front.emission.a = 0x10000;
+
+    c->lighting.lightModel.ambient.r =
+    c->lighting.lightModel.ambient.g =
+    c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f);
+    c->lighting.lightModel.ambient.a = 0x10000;
+
+    c->lighting.colorMaterial.face = GL_FRONT_AND_BACK;
+    c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE;
+
+    c->fog.mode = GL_EXP;
+    c->fog.fog = fog_exp;
+    c->fog.density = 0x10000;
+    c->fog.end = 0x10000;
+    c->fog.invEndMinusStart = 0x10000;
+
+    invalidate_lighting(c);
+       
+    c->rasterizer.procs.shadeModel(c, GL_SMOOTH);
+    c->lighting.shadeModel = GL_SMOOTH;
+}
+
+void ogles_uninit_light(ogles_context_t* c)
+{
+}
+
+static inline int32_t clampF(GLfixed f) CONST;
+int32_t clampF(GLfixed f) {
+    f = (f & ~(f>>31));
+    if (f >= 0x10000)
+        f = 0x10000;
+    return f;
+}
+
+static GLfixed fog_linear(ogles_context_t* c, GLfixed z) {
+    return clampF(gglMulx((c->fog.end - z), c->fog.invEndMinusStart));
+}
+
+static GLfixed fog_exp(ogles_context_t* c, GLfixed z) {
+    const float e = fixedToFloat(gglMulx(c->fog.density, z));
+    return clampF(gglFloatToFixed(fastexpf(-e)));
+}
+
+static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) {
+    const float e = fixedToFloat(gglMulx(c->fog.density, z));
+    return clampF(gglFloatToFixed(fastexpf(-e*e)));
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark math helpers
+#endif
+
+static inline
+void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) {
+    d[0] = gglMulx(m[0], s);
+    d[1] = gglMulx(m[1], s);
+    d[2] = gglMulx(m[2], s);
+}
+
+static inline
+void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulAddx(m[0], s, a[0]);
+    d[1] = gglMulAddx(m[1], s, a[1]);
+    d[2] = gglMulAddx(m[2], s, a[2]);
+}
+
+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));
+    }
+}
+
+static inline
+void vmla3(GLfixed* d,
+        const GLfixed* m0, const GLfixed* m1, const GLfixed* a)
+{
+    d[0] = gglMulAddx(m0[0], m1[0], a[0]);
+    d[1] = gglMulAddx(m0[1], m1[1], a[1]);
+    d[2] = gglMulAddx(m0[2], m1[2], a[2]);
+}
+
+static inline
+void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) {
+    d[0] = gglMulx(m0[0], m1[0]);
+    d[1] = gglMulx(m0[1], m1[1]);
+    d[2] = gglMulx(m0[2], m1[2]);
+}
+
+void vnorm3(GLfixed* d, const GLfixed* a)
+{
+    // we must take care of overflows when normalizing a vector
+    GLfixed n;
+    int32_t x = a[0];   x = x>=0 ? x : -x;
+    int32_t y = a[1];   y = y>=0 ? y : -y;
+    int32_t z = a[2];   z = z>=0 ? z : -z;
+    if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) {
+        // in this case this will all fit on 32 bits
+        n = x*x + y*y + z*z;
+        n = gglSqrtRecipx(n);
+        n <<= 8;
+    } else {
+        // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117)
+        n = vsquare3(x, y, z);
+        n = gglSqrtRecipx(n);
+    }
+    vscale3(d, a, n);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark lighting equations
+#endif
+
+static inline void light_picker(ogles_context_t* c)
+{
+    if (ggl_likely(!c->lighting.enable)) {
+        c->lighting.lightVertex = lightVertexNop;
+        return;
+    }
+    if (c->lighting.colorMaterial.enable) {
+        c->lighting.lightVertex = lightVertexMaterial;
+    } else {
+        c->lighting.lightVertex = lightVertex;
+    }
+}
+
+static inline void validate_light_mvi(ogles_context_t* c)
+{
+    uint32_t en = c->lighting.enabledLights;
+    while (en) {
+        const int i = 31 - gglClz(en);
+        en &= ~(1<<i);
+        light_t& l = c->lighting.lights[i];
+        c->transforms.mvui.point3(&c->transforms.mvui,
+                &l.objPosition, &l.position);
+        vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
+    }
+}
+
+static inline void validate_light(ogles_context_t* c)
+{
+    // if colorMaterial is enabled, we get the color from the vertex
+    if (!c->lighting.colorMaterial.enable) {
+        material_t& material = c->lighting.front;
+        uint32_t en = c->lighting.enabledLights;
+        while (en) {
+            const int i = 31 - gglClz(en);
+            en &= ~(1<<i);
+            light_t& l = c->lighting.lights[i];
+            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;
+            
+            l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0;
+            if (l.rConstAttenuation)
+                l.rConstAttenuation = gglRecipFast(l.attenuation[0]);
+        }
+        // emission and ambient for the whole scene
+        vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
+                c->lighting.lightModel.ambient.v,
+                material.ambient.v, 
+                material.emission.v);
+        c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;
+    }
+    validate_light_mvi(c);
+}
+
+void invalidate_lighting(ogles_context_t* c)
+{
+    // TODO: pick lightVertexValidate or lightVertexValidateMVI
+    // instead of systematically the heavier lightVertexValidate()
+    c->lighting.lightVertex = lightVertexValidate;
+}
+
+void ogles_invalidate_lighting_mvui(ogles_context_t* c)
+{
+    invalidate_lighting(c);
+}
+
+void lightVertexNop(ogles_context_t*, vertex_t* v)
+{
+    // we should never end-up here
+}
+
+void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v)
+{
+    validate_light_mvi(c);
+    light_picker(c);
+    c->lighting.lightVertex(c, v);
+}
+
+void lightVertexValidate(ogles_context_t* c, vertex_t* v)
+{
+    validate_light(c);
+    light_picker(c);
+    c->lighting.lightVertex(c, v);
+}
+
+void lightVertexMaterial(ogles_context_t* c, vertex_t* v)
+{
+    // fetch the material color
+    const GLvoid* cp = c->arrays.color.element(
+            v->index & vertex_cache_t::INDEX_MASK);
+    c->arrays.color.fetch(c, v->color.v, cp);
+
+    // acquire the color-material from the vertex
+    material_t& material = c->lighting.front;
+    material.ambient =
+    material.diffuse = v->color;
+    // implicit arguments need to be computed per/vertex
+    uint32_t en = c->lighting.enabledLights;
+    while (en) {
+        const int i = 31 - gglClz(en);
+        en &= ~(1<<i);
+        light_t& l = c->lighting.lights[i];
+        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);
+    }
+    // emission and ambient for the whole scene
+    vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
+            c->lighting.lightModel.ambient.v,
+            material.ambient.v, 
+            material.emission.v);
+    c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;
+
+    // now we can light our vertex as usual
+    lightVertex(c, v);
+}
+
+void lightVertex(ogles_context_t* c, vertex_t* v)
+{
+    // emission and ambient for the whole scene
+    vec4_t r = c->lighting.implicitSceneEmissionAndAmbient;
+
+    uint32_t en = c->lighting.enabledLights;
+    if (ggl_likely(en)) {
+        // since we do the lighting in object-space, we don't need to
+        // transform each normal. However, we might still have to normalize
+        // it if GL_NORMALIZE is enabled.
+        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)
+            vnorm3(n.v, n.v);
+
+        const material_t& material = c->lighting.front;
+        const int twoSide = c->lighting.lightModel.twoSide;
+
+        while (en) {
+            const int i = 31 - gglClz(en);
+            en &= ~(1<<i);
+            const light_t& l = c->lighting.lights[i];
+            
+            vec4_t d, t;
+            GLfixed s;
+            GLfixed sqDist = 0x10000;
+
+            // compute vertex-to-light vector
+            if (ggl_unlikely(l.position.w)) {
+                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                sqDist = dot3(d.v, d.v);
+                vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
+            } else {
+                // TODO: avoid copy here
+                d = l.normalizedObjPosition;
+            }
+
+            // ambient & diffuse
+            s = dot3(n.v, d.v);
+            s = (s<0) ? (twoSide?(-s):0) : s;
+            vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v);
+            
+            // specular
+            if (ggl_unlikely(s && l.implicitSpecular.v[3])) {
+                vec4_t h;
+                h.x = d.x;
+                h.y = d.y;
+                h.z = d.z + 0x10000;
+                vnorm3(h.v, h.v);
+                s = dot3(n.v, h.v);
+                s = (s<0) ? (twoSide?(-s):0) : s;
+                if (s > 0) {
+                    s = gglPowx(s, material.shininess);
+                    vsa3(t.v, l.implicitSpecular.v, s, t.v);
+                }
+            }
+
+            // spot
+            if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) {
+                GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v);
+                if (spotAtt >= l.spotCutoffCosine) {
+                    vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp));
+                }
+            }
+
+            // attenuation
+            if (ggl_unlikely(l.position.w)) {
+                if (l.rConstAttenuation) {
+                    s = l.rConstAttenuation;
+                } else {
+                    s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]);
+                    if (l.attenuation[1])
+                        s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s);
+                    s = gglRecipFast(s);
+                }
+                vscale3(t.v, t.v, s);
+            }
+
+            r.r += t.r;
+            r.g += t.g;
+            r.b += t.b;
+        }
+    }
+    v->color.r = gglClampx(r.r);
+    v->color.g = gglClampx(r.g);
+    v->color.b = gglClampx(r.b);
+    v->color.a = gglClampx(r.a);
+    v->flags |= vertex_t::LIT;
+}
+
+static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c)
+{
+    if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE;
+    invalidate_lighting(c);
+}
+
+static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c)
+{
+    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    light_t& light = c->lighting.lights[i-GL_LIGHT0];
+    const GLfixed kDegToRad = GLfixed((M_PI * gglIntToFixed(1)) / 180.0f);
+    switch (pname) {
+    case GL_SPOT_EXPONENT:
+        if (GGLfixed(param) >= gglIntToFixed(128)) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        light.spotExp = param;
+        break;
+    case GL_SPOT_CUTOFF:
+        if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        light.spotCutoff = param;
+        light.spotCutoffCosine = 
+                gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param));
+        break;
+    case GL_CONSTANT_ATTENUATION:
+        if (param < 0) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        light.attenuation[0] = param;
+        break;
+    case GL_LINEAR_ATTENUATION:
+        if (param < 0) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        light.attenuation[1] = param;
+        break;
+    case GL_QUADRATIC_ATTENUATION:
+        if (param < 0) {
+            ogles_error(c, GL_INVALID_VALUE);
+            return;
+        }
+        light.attenuation[2] = param;
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    invalidate_lighting(c);
+}
+
+static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c)
+{
+    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    GLfixed* what;
+    light_t& light = c->lighting.lights[i-GL_LIGHT0];
+    switch (pname) {
+    case GL_AMBIENT:
+        what = light.ambient.v;
+        break;
+    case GL_DIFFUSE:
+        what = light.diffuse.v;
+        break;
+    case GL_SPECULAR:
+        what = light.specular.v;
+        break;
+    case GL_POSITION: {
+        ogles_validate_transform(c, transform_state_t::MODELVIEW);
+        transform_t& mv = c->transforms.modelview.transform;
+        memcpy(light.position.v, params, sizeof(light.position.v));
+        mv.point4(&mv, &light.position, &light.position);
+        invalidate_lighting(c);
+        return;
+    }
+    case GL_SPOT_DIRECTION: {
+        ogles_validate_transform(c, transform_state_t::MVUI);
+        transform_t& mvui = c->transforms.mvui;
+        mvui.point3(&mvui, &light.spotDir, (vec4_t*)params);
+        vnorm3(light.normalizedSpotDir.v, light.spotDir.v);
+        invalidate_lighting(c);
+        return;
+    }
+    default:
+        lightx(i, pname, params[0], c);
+        return;
+    }
+    what[0] = params[0];
+    what[1] = params[1];
+    what[2] = params[2];
+    what[3] = params[3];
+    invalidate_lighting(c);
+}
+
+static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c)
+{
+    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (ggl_unlikely(pname != GL_SHININESS)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->lighting.front.shininess = param;
+    invalidate_lighting(c);
+}
+
+static void fogx(GLenum pname, GLfixed param, ogles_context_t* c)
+{
+    switch (pname) {
+    case GL_FOG_DENSITY:
+        if (param >= 0) {
+            c->fog.density = param;
+            break;
+        }
+        ogles_error(c, GL_INVALID_VALUE);
+        break;
+    case GL_FOG_START:
+        c->fog.start = gglClampx(param);
+        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
+        break;
+    case GL_FOG_END:
+        c->fog.end = gglClampx(param);
+        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
+        break;
+    case GL_FOG_MODE:
+        switch (param) {
+        case GL_LINEAR:
+            c->fog.mode = param;
+            c->fog.fog = fog_linear;
+            break;
+        case GL_EXP:
+            c->fog.mode = param;
+            c->fog.fog = fog_exp;
+            break;
+        case GL_EXP2:
+            c->fog.mode = param;
+            c->fog.fog = fog_exp2;
+            break;
+        default:
+            ogles_error(c, GL_INVALID_ENUM);
+            break;
+        }
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        break;
+    }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+#if 0
+#pragma mark -
+#pragma mark lighting APIs
+#endif
+
+void glShadeModel(GLenum mode)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->lighting.shadeModel = mode;
+}
+
+void glLightModelf(GLenum pname, GLfloat param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    lightModelx(pname, gglFloatToFixed(param), c);
+}
+
+void glLightModelx(GLenum pname, GLfixed param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    lightModelx(pname, param, c);
+}
+
+void glLightModelfv(GLenum pname, const GLfloat *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
+        lightModelx(pname, gglFloatToFixed(params[0]), c);
+        return;
+    }
+
+    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]);
+    c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]);
+    c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]);
+    c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]);
+    invalidate_lighting(c);
+}
+
+void glLightModelxv(GLenum pname, const GLfixed *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
+        lightModelx(pname, params[0], c);
+        return;
+    }
+
+    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    c->lighting.lightModel.ambient.r = params[0];
+    c->lighting.lightModel.ambient.g = params[1];
+    c->lighting.lightModel.ambient.b = params[2];
+    c->lighting.lightModel.ambient.a = params[3];
+    invalidate_lighting(c);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void glLightf(GLenum i, GLenum pname, GLfloat param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    lightx(i, pname, gglFloatToFixed(param), c);
+}
+
+void glLightx(GLenum i, GLenum pname, GLfixed param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    lightx(i, pname, param, c);
+}
+
+void glLightfv(GLenum i, GLenum pname, const GLfloat *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    switch (pname) {
+    case GL_SPOT_EXPONENT:
+    case GL_SPOT_CUTOFF:
+    case GL_CONSTANT_ATTENUATION:
+    case GL_LINEAR_ATTENUATION:
+    case GL_QUADRATIC_ATTENUATION:
+        lightx(i, pname, gglFloatToFixed(params[0]), c);
+        return;
+    }
+
+    GLfixed paramsx[4];
+    paramsx[0] = gglFloatToFixed(params[0]);
+    paramsx[1] = gglFloatToFixed(params[1]);
+    paramsx[2] = gglFloatToFixed(params[2]);
+    if (pname != GL_SPOT_DIRECTION)
+        paramsx[3] = gglFloatToFixed(params[3]);
+
+    lightxv(i, pname, paramsx, c);
+}
+
+void glLightxv(GLenum i, GLenum pname, const GLfixed *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    lightxv(i, pname, params, c);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void glMaterialf(GLenum face, GLenum pname, GLfloat param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    materialx(face, pname, gglFloatToFixed(param), c);
+}
+
+void glMaterialx(GLenum face, GLenum pname, GLfixed param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    materialx(face, pname, param, c);
+}
+
+void glMaterialfv(
+    GLenum face, GLenum pname, const GLfloat *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    GLfixed* what=0;
+    GLfixed* other=0;
+    switch (pname) {
+    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
+    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
+    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
+    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
+    case GL_AMBIENT_AND_DIFFUSE:
+        what  = c->lighting.front.ambient.v; break;
+        other = c->lighting.front.diffuse.v; break;
+        break;
+    case GL_SHININESS:
+        c->lighting.front.shininess = gglFloatToFixed(params[0]);
+        invalidate_lighting(c);
+        return;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    what[0] = gglFloatToFixed(params[0]);
+    what[1] = gglFloatToFixed(params[1]);
+    what[2] = gglFloatToFixed(params[2]);
+    what[3] = gglFloatToFixed(params[3]);
+    if (other) {
+        other[0] = what[0];
+        other[1] = what[1];
+        other[2] = what[2];
+        other[3] = what[3];
+    }
+    invalidate_lighting(c);
+}
+
+void glMaterialxv(
+    GLenum face, GLenum pname, const GLfixed *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    GLfixed* what=0;
+    GLfixed* other=0;
+    switch (pname) {
+    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
+    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
+    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
+    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
+    case GL_AMBIENT_AND_DIFFUSE:
+        what = c->lighting.front.ambient.v; break;
+        other= c->lighting.front.diffuse.v; break;
+        break;
+    case GL_SHININESS:
+        c->lighting.front.shininess = gglFloatToFixed(params[0]);
+        invalidate_lighting(c);
+        return;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    what[0] = params[0];
+    what[1] = params[1];
+    what[2] = params[2];
+    what[3] = params[3];
+    if (other) {
+        other[0] = what[0];
+        other[1] = what[1];
+        other[2] = what[2];
+        other[3] = what[3];
+    }
+    invalidate_lighting(c);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark fog
+#endif
+
+void glFogf(GLenum pname, GLfloat param) {
+    ogles_context_t* c = ogles_context_t::get();
+    GLfixed paramx = (GLfixed)param;
+    if (pname != GL_FOG_MODE)
+        paramx = gglFloatToFixed(param);
+    fogx(pname, paramx, c);
+}
+
+void glFogx(GLenum pname, GLfixed param) {
+    ogles_context_t* c = ogles_context_t::get();
+    fogx(pname, param, c);
+}
+
+void glFogfv(GLenum pname, const GLfloat *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (pname != GL_FOG_COLOR) {
+        GLfixed paramx = (GLfixed)params[0];
+        if (pname != GL_FOG_MODE)
+            paramx = gglFloatToFixed(params[0]);
+        fogx(pname, paramx, c);
+        return;
+    }
+    GLfixed paramsx[4];
+    paramsx[0] = gglFloatToFixed(params[0]);
+    paramsx[1] = gglFloatToFixed(params[1]);
+    paramsx[2] = gglFloatToFixed(params[2]);
+    paramsx[3] = gglFloatToFixed(params[3]);
+    c->rasterizer.procs.fogColor3xv(c, paramsx);
+}
+
+void glFogxv(GLenum pname, const GLfixed *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (pname != GL_FOG_COLOR) {
+        fogx(pname, params[0], c);
+        return;
+    }
+    c->rasterizer.procs.fogColor3xv(c, params);
+}
diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h
new file mode 100644
index 0000000..6dae25f
--- /dev/null
+++ b/opengl/libagl/light.h
@@ -0,0 +1,38 @@
+/* libs/opengles/light.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_LIGHT_H
+#define ANDROID_OPENGLES_LIGHT_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace gl {
+struct ogles_context_t;
+};
+
+void ogles_init_light(ogles_context_t* c);
+void ogles_uninit_light(ogles_context_t* c);
+void ogles_invalidate_lighting_mvui(ogles_context_t* c);
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_LIGHT_H
+
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
new file mode 100644
index 0000000..441da38
--- /dev/null
+++ b/opengl/libagl/matrix.cpp
@@ -0,0 +1,1144 @@
+/* libs/opengles/matrix.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "light.h"
+
+#if defined(__arm__) && defined(__thumb__)
+#warning "matrix.cpp should not be compiled in thumb on ARM."
+#endif
+
+#define I(_i, _j) ((_j)+ 4*(_i))
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static const GLfloat gIdentityf[16] = { 1,0,0,0,
+                                        0,1,0,0,
+                                        0,0,1,0,
+                                        0,0,0,1 };
+
+static const matrixx_t gIdentityx = { 
+            {   0x10000,0,0,0,
+                0,0x10000,0,0,
+                0,0,0x10000,0,
+                0,0,0,0x10000
+            }
+        };
+
+static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o);
+static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o);
+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);
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void ogles_init_matrix(ogles_context_t* c)
+{
+    c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH);
+    c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH);
+    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
+        c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH);
+
+    c->transforms.current = &c->transforms.modelview;
+    c->transforms.matrixMode = GL_MODELVIEW;
+    c->transforms.dirty =   transform_state_t::VIEWPORT | 
+                            transform_state_t::MVUI |
+                            transform_state_t::MVIT |
+                            transform_state_t::MVP;
+    c->transforms.mvp.loadIdentity();
+    c->transforms.mvp4.loadIdentity();
+    c->transforms.mvit4.loadIdentity();
+    c->transforms.mvui.loadIdentity();
+    c->transforms.vpt.loadIdentity();
+    c->transforms.vpt.zNear = 0.0f;
+    c->transforms.vpt.zFar  = 1.0f;
+}
+
+void ogles_uninit_matrix(ogles_context_t* c)
+{
+    c->transforms.modelview.uninit();
+    c->transforms.projection.uninit();
+    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
+        c->transforms.texture[i].uninit();
+}
+
+static void validate_perspective(ogles_context_t* c, vertex_t* v)
+{
+    const uint32_t enables = c->rasterizer.state.enables;
+    c->arrays.perspective = (c->clipPlanes.enable) ?
+        ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D;
+    if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
+        c->arrays.perspective = (c->clipPlanes.enable) ?
+            ogles_vertex_clipAllPerspective3DZ : ogles_vertex_perspective3DZ;
+    }
+    if ((c->arrays.vertex.size != 4) &&
+        (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) {
+        c->arrays.perspective = ogles_vertex_perspective2D;
+    }
+    c->arrays.perspective(c, v);
+}
+
+void ogles_invalidate_perspective(ogles_context_t* c)
+{
+    c->arrays.perspective = validate_perspective;
+}
+
+void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want)
+{
+    int dirty = c->transforms.dirty & want;
+
+    // Validate the modelview
+    if (dirty & transform_state_t::MODELVIEW) {
+        c->transforms.modelview.validate();
+    }
+
+    // Validate the projection stack (in fact, it's never needed)
+    if (dirty & transform_state_t::PROJECTION) {
+        c->transforms.projection.validate();
+    }
+
+    // Validate the viewport transformation
+    if (dirty & transform_state_t::VIEWPORT) {
+        vp_transform_t& vpt = c->transforms.vpt;
+        vpt.transform.matrix.load(vpt.matrix);
+        vpt.transform.picker();
+    }
+
+    // We need to update the mvp (used to transform each vertex)
+    if (dirty & transform_state_t::MVP) {
+        c->transforms.update_mvp();
+        // invalidate perspective (divide by W) and view volume clipping
+        ogles_invalidate_perspective(c);
+    }
+
+    // Validate the mvui (for normal transformation)
+    if (dirty & transform_state_t::MVUI) {
+        c->transforms.update_mvui();
+        ogles_invalidate_lighting_mvui(c);
+    }
+
+    // Validate the texture stack
+    if (dirty & transform_state_t::TEXTURE) {
+        for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
+            c->transforms.texture[i].validate();
+    }
+
+    // Validate the mvit4 (user-clip planes)
+    if (dirty & transform_state_t::MVIT) {
+        c->transforms.update_mvit();
+    }
+
+    c->transforms.dirty &= ~want;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark transform_t
+#endif
+
+void transform_t::loadIdentity() {
+    matrix = gIdentityx;
+    flags = 0;
+    ops = OP_IDENTITY;
+    point2 = point2__nop;
+    point3 = point3__nop;
+    point4 = point4__nop;
+}
+
+
+static inline
+int notZero(GLfixed v) {
+    return abs(v) & ~0x3;
+}
+
+static inline
+int notOne(GLfixed v) {
+    return notZero(v - 0x10000);
+}
+
+void transform_t::picker()
+{
+    const GLfixed* const m = matrix.m;
+
+    // XXX: picker needs to be smarter
+    flags = 0;
+    ops = OP_ALL;
+    point2 = point2__generic;
+    point3 = point3__generic;
+    point4 = point4__generic;
+    
+    // find out if this is a 2D projection
+    if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) {
+        flags |= FLAGS_2D_PROJECTION;
+    }
+}
+
+void mvui_transform_t::picker()
+{
+    flags = 0;
+    ops = OP_ALL;
+    point3 = normal__generic;
+}
+
+void transform_t::dump(const char* what)
+{
+    GLfixed const * const m = matrix.m;
+    LOGD("%s:", what);
+    for (int i=0 ; i<4 ; i++)
+        LOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n",
+            m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)],
+            fixedToFloat(m[I(0,i)]),
+            fixedToFloat(m[I(1,i)]), 
+            fixedToFloat(m[I(2,i)]),
+            fixedToFloat(m[I(3,i)]));
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark matrixx_t
+#endif
+
+void matrixx_t::load(const matrixf_t& rhs) {
+    GLfixed* xp = m;
+    GLfloat const* fp = rhs.elements();
+    unsigned int i = 16;
+    do {
+        const GLfloat f = *fp++;
+        *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f);
+    } while (--i);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark matrixf_t
+#endif
+
+void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs)
+{
+    GLfloat const* const m = lhs.m;
+    for (int i=0 ; i<4 ; i++) {
+        register const float rhs_i0 = rhs.m[ I(i,0) ];
+        register float ri0 = m[ I(0,0) ] * rhs_i0;
+        register float ri1 = m[ I(0,1) ] * rhs_i0;
+        register float ri2 = m[ I(0,2) ] * rhs_i0;
+        register float ri3 = m[ I(0,3) ] * rhs_i0;
+        for (int j=1 ; j<4 ; j++) {
+            register const float rhs_ij = rhs.m[ I(i,j) ];
+            ri0 += m[ I(j,0) ] * rhs_ij;
+            ri1 += m[ I(j,1) ] * rhs_ij;
+            ri2 += m[ I(j,2) ] * rhs_ij;
+            ri3 += m[ I(j,3) ] * rhs_ij;
+        }
+        r.m[ I(i,0) ] = ri0;
+        r.m[ I(i,1) ] = ri1;
+        r.m[ I(i,2) ] = ri2;
+        r.m[ I(i,3) ] = ri3;
+    }
+}
+
+void matrixf_t::dump(const char* what) {
+    LOGD("%s", what);
+    LOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]);
+    LOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]);
+    LOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]);
+    LOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]);
+}
+
+void matrixf_t::loadIdentity() {
+    memcpy(m, gIdentityf, sizeof(m));
+}
+
+void matrixf_t::set(const GLfixed* rhs) {
+    load(rhs);
+}
+
+void matrixf_t::set(const GLfloat* rhs) {
+    load(rhs);
+}
+
+void matrixf_t::load(const GLfixed* rhs) {
+    GLfloat* fp = m;
+    unsigned int i = 16;
+    do {
+        *fp++ = fixedToFloat(*rhs++);
+    } while (--i);
+}
+
+void matrixf_t::load(const GLfloat* rhs) {
+    memcpy(m, rhs, sizeof(m));
+}
+
+void matrixf_t::load(const matrixf_t& rhs) {
+    operator = (rhs);
+}
+
+void matrixf_t::multiply(const matrixf_t& rhs) {
+    matrixf_t r;
+    multiply(r, *this, rhs);
+    operator = (r);
+}
+
+void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) {
+    for (int i=0 ; i<4 ; i++) {
+        m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z;
+    }
+}
+
+void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) {
+    for (int i=0 ; i<4 ; i++) {
+        m[  i] *= x;
+        m[4+i] *= y;
+        m[8+i] *= z;
+    }
+}
+
+void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
+{
+    matrixf_t rotation;
+    GLfloat* r = rotation.m;
+    GLfloat c, s;
+    r[3] = 0;   r[7] = 0;   r[11]= 0;
+    r[12]= 0;   r[13]= 0;   r[14]= 0;   r[15]= 1;
+    a *= GLfloat(M_PI / 180.0f);
+    sincosf(a, &s, &c);
+    if (isOnef(x) && isZerof(y) && isZerof(z)) {
+        r[5] = c;   r[10]= c;
+        r[6] = s;   r[9] = -s;
+        r[1] = 0;   r[2] = 0;
+        r[4] = 0;   r[8] = 0;
+        r[0] = 1;
+    } else if (isZerof(x) && isOnef(y) && isZerof(z)) {
+        r[0] = c;   r[10]= c;
+        r[8] = s;   r[2] = -s;
+        r[1] = 0;   r[4] = 0;
+        r[6] = 0;   r[9] = 0;
+        r[5] = 1;
+    } else if (isZerof(x) && isZerof(y) && isOnef(z)) {
+        r[0] = c;   r[5] = c;
+        r[1] = s;   r[4] = -s;
+        r[2] = 0;   r[6] = 0;
+        r[8] = 0;   r[9] = 0;
+        r[10]= 1;
+    } else {
+        const GLfloat len = sqrtf(x*x + y*y + z*z);
+        if (!isOnef(len)) {
+            const GLfloat recipLen = reciprocalf(len);
+            x *= recipLen;
+            y *= recipLen;
+            z *= recipLen;
+        }
+        const GLfloat nc = 1.0f - c;
+        const GLfloat xy = x * y;
+        const GLfloat yz = y * z;
+        const GLfloat zx = z * x;
+        const GLfloat xs = x * s;
+        const GLfloat ys = y * s;
+        const GLfloat zs = z * s;		
+        r[ 0] = x*x*nc +  c;    r[ 4] =  xy*nc - zs;    r[ 8] =  zx*nc + ys;
+        r[ 1] =  xy*nc + zs;    r[ 5] = y*y*nc +  c;    r[ 9] =  yz*nc - xs;
+        r[ 2] =  zx*nc - ys;    r[ 6] =  yz*nc + xs;    r[10] = z*z*nc +  c;
+    }
+    multiply(rotation);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark matrix_stack_t
+#endif
+
+void matrix_stack_t::init(int depth) {
+    stack = new matrixf_t[depth];
+    ops = new uint8_t[depth];
+    maxDepth = depth;
+    depth = 0;
+    dirty = 0;
+    loadIdentity();
+}
+
+void matrix_stack_t::uninit() {
+    delete [] stack;
+    delete [] ops;
+}
+
+void matrix_stack_t::loadIdentity() {
+    transform.loadIdentity();
+    stack[depth].loadIdentity();
+    ops[depth] = OP_IDENTITY;
+}
+
+void matrix_stack_t::load(const GLfixed* rhs)
+{   
+    memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m));
+    stack[depth].load(rhs);
+    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
+}
+
+void matrix_stack_t::load(const GLfloat* rhs)
+{
+    stack[depth].load(rhs);
+    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
+}
+
+void matrix_stack_t::multiply(const matrixf_t& rhs)
+{    
+    stack[depth].multiply(rhs);
+    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
+}
+
+void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z)
+{
+    stack[depth].translate(x,y,z);
+    ops[depth] |= OP_TRANSLATE;
+}
+
+void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z)
+{
+    stack[depth].scale(x,y,z);
+    if (x==y && y==z) {
+        ops[depth] |= OP_UNIFORM_SCALE;
+    } else {
+        ops[depth] |= OP_SCALE;
+    }
+}
+
+void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
+{
+    stack[depth].rotate(a,x,y,z);
+    ops[depth] |= OP_ROTATE;
+}
+
+void matrix_stack_t::validate()
+{
+    if (dirty & DO_FLOAT_TO_FIXED) {
+        transform.matrix.load(top());
+    }
+    if (dirty & DO_PICKER) {
+        transform.picker();
+    }
+    dirty = 0;
+}
+
+GLint matrix_stack_t::push()
+{
+    if (depth >= (maxDepth-1)) {
+        return GL_STACK_OVERFLOW;
+    }
+    stack[depth+1] = stack[depth];
+    ops[depth+1] = ops[depth];
+    depth++;
+    return 0;
+}
+
+GLint matrix_stack_t::pop()
+{
+    if (depth == 0) {
+        return GL_STACK_UNDERFLOW;
+    }
+    depth--;
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark vp_transform_t
+#endif
+
+void vp_transform_t::loadIdentity() {
+    transform.loadIdentity();
+    matrix.loadIdentity();
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark transform_state_t
+#endif
+
+void transform_state_t::invalidate()
+{
+    switch (matrixMode) {
+    case GL_MODELVIEW:  dirty |= MODELVIEW  | MVP | MVUI | MVIT;    break;
+    case GL_PROJECTION: dirty |= PROJECTION | MVP;                  break;
+    case GL_TEXTURE:    dirty |= TEXTURE    | MVP;                  break;
+    }
+    current->dirty =    matrix_stack_t::DO_PICKER |
+                        matrix_stack_t::DO_FLOAT_TO_FIXED;
+}
+
+void transform_state_t::update_mvp()
+{
+    matrixf_t temp_mvp;
+    matrixf_t::multiply(temp_mvp, projection.top(), modelview.top());
+    mvp4.matrix.load(temp_mvp);
+    mvp4.picker();
+
+    if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) {
+        // the mvp matrix doesn't transform W, in this case we can
+        // premultiply it with the viewport transformation. In addition to
+        // being more efficient, this is also much more accurate and in fact
+        // is needed for 2D drawing with a resulting 1:1 mapping.
+        matrixf_t mvpv;
+        matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp);
+        mvp.matrix.load(mvpv);
+        mvp.picker();
+    } else {
+        mvp = mvp4;
+    }
+}
+
+static inline 
+GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
+    return a*d - b*c;
+}
+
+static inline
+GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
+    return b*c - a*d;
+}
+
+static __attribute__((noinline))
+void invert(GLfloat* inverse, const GLfloat* src)
+{
+    double t;
+    int i, j, k, swap;
+    GLfloat tmp[4][4];
+    
+    memcpy(inverse, gIdentityf, sizeof(gIdentityf));
+    memcpy(tmp, src, sizeof(GLfloat)*16);
+    
+    for (i = 0; i < 4; i++) {
+        // look for largest element in column
+        swap = i;
+        for (j = i + 1; j < 4; j++) {
+            if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
+                swap = j;
+            }
+        }
+        
+        if (swap != i) {
+            /* swap rows. */
+            for (k = 0; k < 4; k++) {
+                t = tmp[i][k];
+                tmp[i][k] = tmp[swap][k];
+                tmp[swap][k] = t;
+                
+                t = inverse[i*4+k];
+                inverse[i*4+k] = inverse[swap*4+k];
+                inverse[swap*4+k] = t;
+            }
+        }
+        
+        t = 1.0f / tmp[i][i];
+        for (k = 0; k < 4; k++) {
+            tmp[i][k] *= t;
+            inverse[i*4+k] *= t;
+        }
+        for (j = 0; j < 4; j++) {
+            if (j != i) {
+                t = tmp[j][i];
+                for (k = 0; k < 4; k++) {
+                    tmp[j][k] -= tmp[i][k]*t;
+                    inverse[j*4+k] -= inverse[i*4+k]*t;
+                }
+            }
+        }
+    }
+}
+
+void transform_state_t::update_mvit()
+{
+    GLfloat r[16];
+    const GLfloat* const mv = modelview.top().elements();
+    invert(r, mv);
+    // convert to fixed-point and transpose
+    GLfixed* const x = mvit4.matrix.m;
+    for (int i=0 ; i<4 ; i++)
+        for (int j=0 ; j<4 ; j++)
+            x[I(i,j)] = gglFloatToFixed(r[I(j,i)]);
+    mvit4.picker();
+}
+
+void transform_state_t::update_mvui()
+{
+    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)]);
+    }
+
+    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);
+    }
+    mvui.picker();
+}
+
+
+// ----------------------------------------------------------------------------
+// transformation and matrices API
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark transformation and matrices API
+#endif
+
+int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y)
+{
+    c->viewport.surfaceport.x = x;
+    c->viewport.surfaceport.y = y;
+
+    ogles_viewport(c, 
+            c->viewport.x,
+            c->viewport.y,
+            c->viewport.w,
+            c->viewport.h);
+
+    ogles_scissor(c,
+            c->viewport.scissor.x,
+            c->viewport.scissor.y,
+            c->viewport.scissor.w,
+            c->viewport.scissor.h);
+
+    return 0;
+}
+
+void ogles_scissor(ogles_context_t* c, 
+        GLint x, GLint y, GLsizei w, GLsizei h)
+{
+    if ((w|h) < 0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    c->viewport.scissor.x = x;
+    c->viewport.scissor.y = y;
+    c->viewport.scissor.w = w;
+    c->viewport.scissor.h = h;
+    
+    x += c->viewport.surfaceport.x;
+    y += c->viewport.surfaceport.y;
+
+    y = c->rasterizer.state.buffers.color.height - (y + h);
+    c->rasterizer.procs.scissor(c, x, y, w, h);
+}
+
+void ogles_viewport(ogles_context_t* c,
+        GLint x, GLint y, GLsizei w, GLsizei h)
+{
+    if ((w|h)<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    c->viewport.x = x;
+    c->viewport.y = y;
+    c->viewport.w = w;
+    c->viewport.h = h;
+
+    x += c->viewport.surfaceport.x;
+    y += c->viewport.surfaceport.y;
+
+    GLint H = c->rasterizer.state.buffers.color.height;
+    GLfloat sx = div2f(w);
+    GLfloat ox = sx + x;
+    GLfloat sy = div2f(h);
+    GLfloat oy = sy - y + (H - h);
+
+    GLfloat near = c->transforms.vpt.zNear;
+    GLfloat far  = c->transforms.vpt.zFar;
+    GLfloat A = div2f(far - near);
+    GLfloat B = div2f(far + near);
+
+    // compute viewport matrix
+    GLfloat* const f = c->transforms.vpt.matrix.editElements();
+    f[0] = sx;  f[4] = 0;   f[ 8] = 0;  f[12] = ox;
+    f[1] = 0;   f[5] =-sy;  f[ 9] = 0;  f[13] = oy;
+    f[2] = 0;   f[6] = 0;   f[10] = A;  f[14] = B;
+    f[3] = 0;   f[7] = 0;   f[11] = 0;  f[15] = 1;
+    c->transforms.dirty |= transform_state_t::VIEWPORT;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark matrix * vertex
+#endif
+
+void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    const GLfixed* const m = mx->matrix.m;
+    const GLfixed rx = rhs->x;
+    const GLfixed ry = rhs->y;
+    lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); 
+    lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]);
+    lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]);
+    lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]);
+}
+
+void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    const GLfixed* const m = mx->matrix.m;
+    const GLfixed rx = rhs->x;
+    const GLfixed ry = rhs->y;
+    const GLfixed rz = rhs->z;
+    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 = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
+}
+
+void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    const GLfixed* const m = mx->matrix.m;
+    const GLfixed rx = rhs->x;
+    const GLfixed ry = rhs->y;
+    const GLfixed rz = rhs->z;
+    const GLfixed rw = rhs->w;
+    lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); 
+    lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]);
+    lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]);
+    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) {
+    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]);
+}
+
+
+void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
+    lhs->z = 0;
+    lhs->w = 0x10000;
+    if (lhs != rhs) {
+        lhs->x = rhs->x;
+        lhs->y = rhs->y;
+    }
+}
+
+void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
+    lhs->w = 0x10000;
+    if (lhs != rhs) {
+        lhs->x = rhs->x;
+        lhs->y = rhs->y;
+        lhs->z = rhs->z;
+    }
+}
+
+void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
+    if (lhs != rhs)
+        *lhs = *rhs;
+}
+
+
+static void frustumf(
+            GLfloat left, GLfloat right, 
+            GLfloat bottom, GLfloat top,
+            GLfloat zNear, GLfloat zFar,
+            ogles_context_t* c)
+    {
+    if (cmpf(left,right) ||
+        cmpf(top, bottom) ||
+        cmpf(zNear, zFar) ||
+        isZeroOrNegativef(zNear) ||
+        isZeroOrNegativef(zFar))
+    {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    const GLfloat r_width  = reciprocalf(right - left);
+    const GLfloat r_height = reciprocalf(top - bottom);
+    const GLfloat r_depth  = reciprocalf(zNear - zFar);
+    const GLfloat x = mul2f(zNear * r_width);
+    const GLfloat y = mul2f(zNear * r_height);
+    const GLfloat A = mul2f((right + left) * r_width);
+    const GLfloat B = (top + bottom) * r_height;
+    const GLfloat C = (zFar + zNear) * r_depth;
+    const GLfloat D = mul2f(zFar * zNear * r_depth);
+    GLfloat f[16];
+    f[ 0] = x;
+    f[ 5] = y;
+    f[ 8] = A;
+    f[ 9] = B;
+    f[10] = C;
+    f[14] = D;
+    f[11] = -1.0f;
+    f[ 1] = f[ 2] = f[ 3] =
+    f[ 4] = f[ 6] = f[ 7] =
+    f[12] = f[13] = f[15] = 0.0f;
+
+    matrixf_t rhs;
+    rhs.set(f);
+    c->transforms.current->multiply(rhs);
+    c->transforms.invalidate();
+}
+
+static void orthof( 
+        GLfloat left, GLfloat right, 
+        GLfloat bottom, GLfloat top,
+        GLfloat zNear, GLfloat zFar,
+        ogles_context_t* c)
+{
+    if (cmpf(left,right) ||
+        cmpf(top, bottom) ||
+        cmpf(zNear, zFar))
+    {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    const GLfloat r_width  = reciprocalf(right - left);
+    const GLfloat r_height = reciprocalf(top - bottom);
+    const GLfloat r_depth  = reciprocalf(zFar - zNear);
+    const GLfloat x =  mul2f(r_width);
+    const GLfloat y =  mul2f(r_height);
+    const GLfloat z = -mul2f(r_depth);
+    const GLfloat tx = -(right + left) * r_width;
+    const GLfloat ty = -(top + bottom) * r_height;
+    const GLfloat tz = -(zFar + zNear) * r_depth;
+    GLfloat f[16];
+    f[ 0] = x;
+    f[ 5] = y;
+    f[10] = z;
+    f[12] = tx;
+    f[13] = ty;
+    f[14] = tz;
+    f[15] = 1.0f;
+    f[ 1] = f[ 2] = f[ 3] =
+    f[ 4] = f[ 6] = f[ 7] =
+    f[ 8] = f[ 9] = f[11] = 0.0f;
+    matrixf_t rhs;
+    rhs.set(f);
+    c->transforms.current->multiply(rhs);
+    c->transforms.invalidate();
+}
+
+static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c)
+{
+    zNear = clampToZerof(zNear > 1 ? 1 : zNear);
+    zFar  = clampToZerof(zFar  > 1 ? 1 : zFar);
+    GLfloat* const f = c->transforms.vpt.matrix.editElements();
+    f[10] = div2f(zFar - zNear);
+    f[14] = div2f(zFar + zNear);
+    c->transforms.dirty |= transform_state_t::VIEWPORT;
+    c->transforms.vpt.zNear = zNear;
+    c->transforms.vpt.zFar  = zFar;
+}
+
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+using namespace android;
+
+void glMatrixMode(GLenum mode)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    matrix_stack_t* stack = 0;
+    switch (mode) {
+    case GL_MODELVIEW:
+        stack = &c->transforms.modelview;
+        break;
+    case GL_PROJECTION:
+        stack = &c->transforms.projection;
+        break;
+    case GL_TEXTURE:
+        stack = &c->transforms.texture[c->textures.active];
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->transforms.matrixMode = mode;
+    c->transforms.current = stack;
+}
+
+void glLoadIdentity()
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->loadIdentity(); // also loads the GLfixed transform
+    c->transforms.invalidate();
+    c->transforms.current->dirty = 0;
+}
+
+void glLoadMatrixf(const GLfloat* m)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->load(m);
+    c->transforms.invalidate();
+}
+
+void glLoadMatrixx(const GLfixed* m)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->load(m); // also loads the GLfixed transform
+    c->transforms.invalidate();
+    c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED;
+}
+
+void glMultMatrixf(const GLfloat* m)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    matrixf_t rhs;
+    rhs.set(m);
+    c->transforms.current->multiply(rhs);
+    c->transforms.invalidate();
+}
+
+void glMultMatrixx(const GLfixed* m)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    matrixf_t rhs;
+    rhs.set(m);
+    c->transforms.current->multiply(rhs);
+    c->transforms.invalidate();
+}
+
+void glPopMatrix()
+{
+    ogles_context_t* c = ogles_context_t::get();
+    GLint err = c->transforms.current->pop();
+    if (ggl_unlikely(err)) {
+        ogles_error(c, err);
+        return;
+    }
+    c->transforms.invalidate();
+}
+
+void glPushMatrix()
+{
+    ogles_context_t* c = ogles_context_t::get();
+    GLint err = c->transforms.current->push();
+    if (ggl_unlikely(err)) {
+        ogles_error(c, err);
+        return;
+    }
+    c->transforms.invalidate();
+}
+
+void glFrustumf(
+        GLfloat left, GLfloat right, 
+        GLfloat bottom, GLfloat top,
+        GLfloat zNear, GLfloat zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    frustumf(left, right, bottom, top, zNear, zFar, c);
+}
+
+void glFrustumx( 
+        GLfixed left, GLfixed right,
+        GLfixed bottom, GLfixed top,
+        GLfixed zNear, GLfixed zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    frustumf( fixedToFloat(left), fixedToFloat(right),
+              fixedToFloat(bottom), fixedToFloat(top),
+              fixedToFloat(zNear), fixedToFloat(zFar),
+              c);
+}
+
+void glOrthof( 
+        GLfloat left, GLfloat right, 
+        GLfloat bottom, GLfloat top,
+        GLfloat zNear, GLfloat zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    orthof(left, right, bottom, top, zNear, zFar, c);
+}
+
+void glOrthox(
+        GLfixed left, GLfixed right,
+        GLfixed bottom, GLfixed top,
+        GLfixed zNear, GLfixed zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    orthof( fixedToFloat(left), fixedToFloat(right),
+            fixedToFloat(bottom), fixedToFloat(top),
+            fixedToFloat(zNear), fixedToFloat(zFar),
+            c);
+}
+
+void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->rotate(a, x, y, z);
+    c->transforms.invalidate();
+}
+
+void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->rotate( 
+            fixedToFloat(a), fixedToFloat(x),
+            fixedToFloat(y), fixedToFloat(z));
+    c->transforms.invalidate();
+}
+
+void glScalef(GLfloat x, GLfloat y, GLfloat z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->scale(x, y, z);
+    c->transforms.invalidate();
+}
+
+void glScalex(GLfixed x, GLfixed y, GLfixed z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->scale(
+            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
+    c->transforms.invalidate();
+}
+
+void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->translate(x, y, z);
+    c->transforms.invalidate();
+}
+
+void glTranslatex(GLfixed x, GLfixed y, GLfixed z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->transforms.current->translate(
+            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
+    c->transforms.invalidate();
+}
+
+void glScissor(GLint x, GLint y, GLsizei w, GLsizei h)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    ogles_scissor(c, x, y, w, h);
+}
+
+void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    ogles_viewport(c, x, y, w, h);
+}
+
+void glDepthRangef(GLclampf zNear, GLclampf zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    depthRangef(zNear, zFar, c);
+}
+
+void glDepthRangex(GLclampx zNear, GLclampx zFar)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c);
+}
+
+void glPolygonOffsetx(GLfixed factor, GLfixed units)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->polygonOffset.factor = factor;
+    c->polygonOffset.units = units;
+}
+
+void glPolygonOffset(GLfloat factor, GLfloat units)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->polygonOffset.factor = gglFloatToFixed(factor);
+    c->polygonOffset.units = gglFloatToFixed(units);
+}
+
+GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    GLbitfield status = 0;
+    GLfloat const* f = c->transforms.current->top().elements();
+    for  (int i=0 ; i<16 ; i++) {
+        if (isnan(f[i]) || isinf(f[i])) {
+            status |= 1<<i;
+            continue;
+        }
+        e[i] = exponent(f[i]) - 7;
+        m[i] = mantissa(f[i]);
+    }
+    return status;
+}
diff --git a/opengl/libagl/matrix.h b/opengl/libagl/matrix.h
new file mode 100644
index 0000000..c9a38a9
--- /dev/null
+++ b/opengl/libagl/matrix.h
@@ -0,0 +1,355 @@
+/* libs/opengles/matrix.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_MATRIX_H
+#define ANDROID_OPENGLES_MATRIX_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include <GLES/gl.h>
+
+namespace android {
+
+const int OGLES_MODELVIEW_STACK_DEPTH   = 16;
+const int OGLES_PROJECTION_STACK_DEPTH  =  2;
+const int OGLES_TEXTURE_STACK_DEPTH     =  2;
+
+void ogles_init_matrix(ogles_context_t*);
+void ogles_uninit_matrix(ogles_context_t*);
+void ogles_invalidate_perspective(ogles_context_t* c);
+void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want);
+
+int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y);
+
+void ogles_scissor(ogles_context_t* c, 
+        GLint x, GLint y, GLsizei w, GLsizei h);
+
+void ogles_viewport(ogles_context_t* c,
+        GLint x, GLint y, GLsizei w, GLsizei h);
+
+inline void ogles_validate_transform(
+        ogles_context_t* c, uint32_t want)
+{
+    if (c->transforms.dirty & want)
+        ogles_validate_transform_impl(c, want);
+}
+
+// ----------------------------------------------------------------------------
+
+inline
+GLfixed vsquare3(GLfixed a, GLfixed b, GLfixed c) 
+{
+#if defined(__arm__) && !defined(__thumb__)
+
+    GLfixed r;
+    int32_t t;
+    asm(
+        "smull %0, %1, %2, %2       \n"
+        "smlal %0, %1, %3, %3       \n"
+        "smlal %0, %1, %4, %4       \n"
+        "movs  %0, %0, lsr #16      \n"
+        "adc   %0, %0, %1, lsl #16  \n"
+        :   "=&r"(r), "=&r"(t) 
+        :   "%r"(a), "r"(b), "r"(c)
+        :   "cc"
+        ); 
+    return r;
+
+#else
+
+    return ((   int64_t(a)*a +
+                int64_t(b)*b +
+                int64_t(c)*c + 0x8000)>>16);
+
+#endif
+}
+
+static inline GLfixed mla2a( GLfixed a0, GLfixed b0,
+                            GLfixed a1, GLfixed b1,
+                            GLfixed c)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    int32_t t;
+    asm(
+        "smull %0, %1, %2, %3       \n"
+        "smlal %0, %1, %4, %5       \n"
+        "add   %0, %6, %0, lsr #16  \n"
+        "add   %0, %0, %1, lsl #16  \n"
+        :   "=&r"(r), "=&r"(t) 
+        :   "%r"(a0), "r"(b0), 
+            "%r"(a1), "r"(b1),
+            "r"(c)
+        :
+        ); 
+    return r;
+    
+#else
+
+    return ((   int64_t(a0)*b0 +
+                int64_t(a1)*b1)>>16) + c;
+
+#endif
+}
+
+static inline GLfixed mla3a( GLfixed a0, GLfixed b0,
+                             GLfixed a1, GLfixed b1,
+                             GLfixed a2, GLfixed b2,
+                             GLfixed c)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    int32_t t;
+    asm(
+        "smull %0, %1, %2, %3       \n"
+        "smlal %0, %1, %4, %5       \n"
+        "smlal %0, %1, %6, %7       \n"
+        "add   %0, %8, %0, lsr #16  \n"
+        "add   %0, %0, %1, lsl #16  \n"
+        :   "=&r"(r), "=&r"(t) 
+        :   "%r"(a0), "r"(b0),
+            "%r"(a1), "r"(b1),
+            "%r"(a2), "r"(b2),
+            "r"(c)
+        :
+        ); 
+    return r;
+    
+#else
+
+    return ((   int64_t(a0)*b0 +
+                int64_t(a1)*b1 +
+                int64_t(a2)*b2)>>16) + c;
+
+#endif
+}
+
+// b0, b1, b2 are signed 16-bit quanities
+// that have been shifted right by 'shift' bits relative to normal
+// S16.16 fixed point
+static inline GLfixed mla3a16( GLfixed a0, int32_t b1b0,
+                               GLfixed a1,
+                               GLfixed a2, int32_t b2,
+                               GLint shift,
+                               GLfixed c)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    asm(
+        "smulwb %0, %1, %2          \n"
+        "smlawt %0, %3, %2, %0      \n" 
+        "smlawb %0, %4, %5, %0      \n"
+        "add    %0, %7, %0, lsl %6  \n"
+        :   "=&r"(r)
+        :   "r"(a0), "r"(b1b0),
+            "r"(a1),
+            "r"(a2), "r"(b2),
+            "r"(shift),
+            "r"(c)
+        :
+        ); 
+    return r;
+    
+#else
+
+    int32_t accum;
+    int16_t b0 = b1b0 & 0xffff;
+    int16_t b1 = (b1b0 >> 16) & 0xffff;
+    accum  = int64_t(a0)*int16_t(b0) >> 16;
+    accum += int64_t(a1)*int16_t(b1) >> 16;
+    accum += int64_t(a2)*int16_t(b2) >> 16;
+    accum = (accum << shift) + c;
+    return accum;
+
+#endif
+}
+
+
+static inline GLfixed mla3a16_btb( GLfixed a0,
+                                   GLfixed a1,
+                                   GLfixed a2,
+                                   int32_t b1b0, int32_t xxb2,
+                                   GLint shift,
+                                   GLfixed c)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    asm(
+        "smulwb %0, %1, %4          \n"
+        "smlawt %0, %2, %4, %0      \n" 
+        "smlawb %0, %3, %5, %0      \n"
+        "add    %0, %7, %0, lsl %6  \n"
+        :   "=&r"(r)
+        :   "r"(a0),
+            "r"(a1),
+            "r"(a2),
+            "r"(b1b0), "r"(xxb2),
+            "r"(shift),
+            "r"(c)
+        :
+        ); 
+    return r;
+    
+#else
+
+    int32_t accum;
+    int16_t b0 =  b1b0        & 0xffff;
+    int16_t b1 = (b1b0 >> 16) & 0xffff;
+    int16_t b2 =  xxb2        & 0xffff;
+    accum  = int64_t(a0)*int16_t(b0) >> 16;
+    accum += int64_t(a1)*int16_t(b1) >> 16;
+    accum += int64_t(a2)*int16_t(b2) >> 16;
+    accum = (accum << shift) + c;
+    return accum;
+
+#endif
+}
+
+static inline GLfixed mla3a16_btt( GLfixed a0,
+                                   GLfixed a1,
+                                   GLfixed a2,
+                                   int32_t b1b0, int32_t b2xx,
+                                   GLint shift,
+                                   GLfixed c)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    asm(
+        "smulwb %0, %1, %4          \n"
+        "smlawt %0, %2, %4, %0      \n" 
+        "smlawt %0, %3, %5, %0      \n"
+        "add    %0, %7, %0, lsl %6  \n"
+        :   "=&r"(r)
+        :   "r"(a0),
+            "r"(a1),
+            "r"(a2),
+            "r"(b1b0), "r"(b2xx),
+            "r"(shift),
+            "r"(c)
+        :
+        ); 
+    return r;
+    
+#else
+
+    int32_t accum;
+    int16_t b0 =  b1b0        & 0xffff;
+    int16_t b1 = (b1b0 >> 16) & 0xffff;
+    int16_t b2 = (b2xx >> 16) & 0xffff;
+    accum  = int64_t(a0)*int16_t(b0) >> 16;
+    accum += int64_t(a1)*int16_t(b1) >> 16;
+    accum += int64_t(a2)*int16_t(b2) >> 16;
+    accum = (accum << shift) + c;
+    return accum;
+
+#endif
+}
+
+static inline GLfixed mla3( GLfixed a0, GLfixed b0,
+                            GLfixed a1, GLfixed b1,
+                            GLfixed a2, GLfixed b2)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    int32_t t;
+    asm(
+        "smull %0, %1, %2, %3       \n"
+        "smlal %0, %1, %4, %5       \n"
+        "smlal %0, %1, %6, %7       \n"
+        "movs  %0, %0, lsr #16      \n"
+        "adc   %0, %0, %1, lsl #16  \n"
+        :   "=&r"(r), "=&r"(t) 
+        :   "%r"(a0), "r"(b0),
+            "%r"(a1), "r"(b1),
+            "%r"(a2), "r"(b2)
+        :   "cc"
+        ); 
+    return r;
+    
+#else
+
+    return ((   int64_t(a0)*b0 +
+                int64_t(a1)*b1 +
+                int64_t(a2)*b2 + 0x8000)>>16);
+
+#endif
+}
+
+static inline GLfixed mla4( GLfixed a0, GLfixed b0,
+                            GLfixed a1, GLfixed b1,
+                            GLfixed a2, GLfixed b2,
+                            GLfixed a3, GLfixed b3)
+{
+#if defined(__arm__) && !defined(__thumb__)
+                            
+    GLfixed r;
+    int32_t t;
+    asm(
+        "smull %0, %1, %2, %3       \n"
+        "smlal %0, %1, %4, %5       \n"
+        "smlal %0, %1, %6, %7       \n"
+        "smlal %0, %1, %8, %9       \n"
+        "movs  %0, %0, lsr #16      \n"
+        "adc   %0, %0, %1, lsl #16  \n"
+        :   "=&r"(r), "=&r"(t) 
+        :   "%r"(a0), "r"(b0),
+            "%r"(a1), "r"(b1),
+            "%r"(a2), "r"(b2),
+            "%r"(a3), "r"(b3)
+        :   "cc"
+        ); 
+    return r;
+    
+#else
+
+    return ((   int64_t(a0)*b0 +
+                int64_t(a1)*b1 +
+                int64_t(a2)*b2 +
+                int64_t(a3)*b3 + 0x8000)>>16);
+
+#endif
+}
+
+inline
+GLfixed dot4(const GLfixed* a, const GLfixed* b) 
+{
+    return mla4(a[0], b[0], a[1], b[1], a[2], b[2], a[3], b[3]);
+}
+
+
+inline
+GLfixed dot3(const GLfixed* a, const GLfixed* b) 
+{
+    return mla3(a[0], b[0], a[1], b[1], a[2], b[2]);
+}
+
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_MATRIX_H
+
diff --git a/opengl/libagl/mipmap.cpp b/opengl/libagl/mipmap.cpp
new file mode 100644
index 0000000..ccd77b7
--- /dev/null
+++ b/opengl/libagl/mipmap.cpp
@@ -0,0 +1,192 @@
+/* libs/opengles/mipmap.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "context.h"
+#include "state.h"
+#include "texture.h"
+#include "TextureObjectManager.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex)
+{
+    int level = 0;
+    const GGLSurface* base = &tex->surface;    
+    const GGLFormat& pixelFormat(c->rasterizer.formats[base->format]);
+
+    int w = base->width;
+    int h = base->height;
+    if ((w&h) == 1)
+        return NO_ERROR;
+
+    w = (w>>1) ? : 1;
+    h = (h>>1) ? : 1;
+
+    while(true) {
+        ++level;
+        const int bpr = w * pixelFormat.size;
+        if (tex->reallocate(level, w, h, w,
+                base->format, base->compressedFormat, bpr) != NO_ERROR) {
+            return NO_MEMORY;
+        }
+    
+        int stride = w;
+        int bs = base->stride;
+        GGLSurface& cur = tex->editMip(level);
+
+        if (base->format == GGL_PIXEL_FORMAT_RGB_565)
+        {
+            uint16_t const * src = (uint16_t const *)base->data;
+            uint16_t* dst = (uint16_t*)cur.data;
+            const uint32_t mask = 0x07E0F81F;
+            for (int y=0 ; y<h ; y++) {
+                size_t offset = (y*2) * bs;
+                for (int x=0 ; x<w ; x++) {
+                    uint32_t p00 = src[offset];
+                    uint32_t p10 = src[offset+1];
+                    uint32_t p01 = src[offset+bs];
+                    uint32_t p11 = src[offset+bs+1];
+                    p00 = (p00 | (p00 << 16)) & mask;
+                    p01 = (p01 | (p01 << 16)) & mask;
+                    p10 = (p10 | (p10 << 16)) & mask;
+                    p11 = (p11 | (p11 << 16)) & mask;
+                    uint32_t grb = ((p00 + p10 + p01 + p11) >> 2) & mask;
+                    uint32_t rgb = (grb & 0xFFFF) | (grb >> 16);
+                    dst[x + y*stride] = rgb;
+                    offset += 2;
+                }
+            }
+        }
+        else if (base->format == GGL_PIXEL_FORMAT_RGBA_5551)
+        {
+            uint16_t const * src = (uint16_t const *)base->data;
+            uint16_t* dst = (uint16_t*)cur.data;
+            for (int y=0 ; y<h ; y++) {
+                size_t offset = (y*2) * bs;
+                for (int x=0 ; x<w ; x++) {
+                    uint32_t p00 = src[offset];
+                    uint32_t p10 = src[offset+1];
+                    uint32_t p01 = src[offset+bs];
+                    uint32_t p11 = src[offset+bs+1];
+                    uint32_t r = ((p00>>11)+(p10>>11)+(p01>>11)+(p11>>11)+2)>>2;
+                    uint32_t g = (((p00>>6)+(p10>>6)+(p01>>6)+(p11>>6)+2)>>2)&0x3F;
+                    uint32_t b = ((p00&0x3E)+(p10&0x3E)+(p01&0x3E)+(p11&0x3E)+4)>>3;
+                    uint32_t a = ((p00&1)+(p10&1)+(p01&1)+(p11&1)+2)>>2;
+                    dst[x + y*stride] = (r<<11)|(g<<6)|(b<<1)|a;
+                    offset += 2;
+                }
+            }
+        }
+        else if (base->format == GGL_PIXEL_FORMAT_RGBA_8888)
+        {
+            uint32_t const * src = (uint32_t const *)base->data;
+            uint32_t* dst = (uint32_t*)cur.data;
+            for (int y=0 ; y<h ; y++) {
+                size_t offset = (y*2) * bs;
+                for (int x=0 ; x<w ; x++) {
+                    uint32_t p00 = src[offset];
+                    uint32_t p10 = src[offset+1];
+                    uint32_t p01 = src[offset+bs];
+                    uint32_t p11 = src[offset+bs+1];
+                    uint32_t rb00 = p00 & 0x00FF00FF;
+                    uint32_t rb01 = p01 & 0x00FF00FF;
+                    uint32_t rb10 = p10 & 0x00FF00FF;
+                    uint32_t rb11 = p11 & 0x00FF00FF;
+                    uint32_t ga00 = (p00 >> 8) & 0x00FF00FF;
+                    uint32_t ga01 = (p01 >> 8) & 0x00FF00FF;
+                    uint32_t ga10 = (p10 >> 8) & 0x00FF00FF;
+                    uint32_t ga11 = (p11 >> 8) & 0x00FF00FF;
+                    uint32_t rb = (rb00 + rb01 + rb10 + rb11)>>2;
+                    uint32_t ga = (ga00 + ga01 + ga10 + ga11)>>2;
+                    uint32_t rgba = (rb & 0x00FF00FF) | ((ga & 0x00FF00FF)<<8);
+                    dst[x + y*stride] = rgba;
+                    offset += 2;
+                }
+            }
+        }
+        else if ((base->format == GGL_PIXEL_FORMAT_RGB_888) ||
+                 (base->format == GGL_PIXEL_FORMAT_LA_88) ||
+                 (base->format == GGL_PIXEL_FORMAT_A_8) ||
+                 (base->format == GGL_PIXEL_FORMAT_L_8))
+        {
+            int skip;
+            switch (base->format) {
+            case GGL_PIXEL_FORMAT_RGB_888:  skip = 3;   break;
+            case GGL_PIXEL_FORMAT_LA_88:    skip = 2;   break;
+            default:                        skip = 1;   break;
+            }
+            uint8_t const * src = (uint8_t const *)base->data;
+            uint8_t* dst = (uint8_t*)cur.data;            
+            bs *= skip;
+            stride *= skip;
+            for (int y=0 ; y<h ; y++) {
+                size_t offset = (y*2) * bs;
+                for (int x=0 ; x<w ; x++) {
+                    for (int c=0 ; c<skip ; c++) {
+                        uint32_t p00 = src[c+offset];
+                        uint32_t p10 = src[c+offset+skip];
+                        uint32_t p01 = src[c+offset+bs];
+                        uint32_t p11 = src[c+offset+bs+skip];
+                        dst[x + y*stride + c] = (p00 + p10 + p01 + p11) >> 2;
+                    }
+                    offset += 2*skip;
+                }
+            }
+        }
+        else if (base->format == GGL_PIXEL_FORMAT_RGBA_4444)
+        {
+            uint16_t const * src = (uint16_t const *)base->data;
+            uint16_t* dst = (uint16_t*)cur.data;
+            for (int y=0 ; y<h ; y++) {
+                size_t offset = (y*2) * bs;
+                for (int x=0 ; x<w ; x++) {
+                    uint32_t p00 = src[offset];
+                    uint32_t p10 = src[offset+1];
+                    uint32_t p01 = src[offset+bs];
+                    uint32_t p11 = src[offset+bs+1];
+                    p00 = ((p00 << 12) & 0x0F0F0000) | (p00 & 0x0F0F);
+                    p10 = ((p10 << 12) & 0x0F0F0000) | (p10 & 0x0F0F);
+                    p01 = ((p01 << 12) & 0x0F0F0000) | (p01 & 0x0F0F);
+                    p11 = ((p11 << 12) & 0x0F0F0000) | (p11 & 0x0F0F);
+                    uint32_t rbga = (p00 + p10 + p01 + p11) >> 2;
+                    uint32_t rgba = (rbga & 0x0F0F) | ((rbga>>12) & 0xF0F0);
+                    dst[x + y*stride] = rgba;
+                    offset += 2;
+                }
+            }
+        } else {
+            LOGE("Unsupported format (%d)", base->format);
+            return BAD_TYPE;
+        }
+
+        // exit condition: we just processed the 1x1 LODs
+        if ((w&h) == 1)
+            break;
+
+        base = &cur;
+        w = (w>>1) ? : 1;
+        h = (h>>1) ? : 1;
+    }
+    return NO_ERROR;
+}
+
+}; // namespace android
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp
new file mode 100644
index 0000000..20e8d37
--- /dev/null
+++ b/opengl/libagl/primitives.cpp
@@ -0,0 +1,1095 @@
+/* libs/opengles/primitives.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "context.h"
+#include "primitives.h"
+#include "light.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "fp.h"
+#include "TextureObjectManager.h"
+
+extern "C" void iterators0032(const void* that,
+        int32_t* it, int32_t c0, int32_t c1, int32_t c2);
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static void primitive_point(ogles_context_t* c, vertex_t* v);
+static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1);
+static void primitive_clip_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void primitive_nop_point(ogles_context_t* c, vertex_t* v);
+static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1);
+static void primitive_nop_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static inline bool cull_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void lerp_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void lerp_texcoords(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void lerp_texcoords_w(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static void clip_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2);
+
+static unsigned int clip_line(ogles_context_t* c,
+        vertex_t* s, vertex_t* p);
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+static void lightTriangleDarkSmooth(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    if (!(v0->flags & vertex_t::LIT)) {
+        v0->flags |= vertex_t::LIT;
+        const GLvoid* cp = c->arrays.color.element(
+                v0->index & vertex_cache_t::INDEX_MASK);
+        c->arrays.color.fetch(c, v0->color.v, cp);
+    }
+    if (!(v1->flags & vertex_t::LIT)) {
+        v1->flags |= vertex_t::LIT;
+        const GLvoid* cp = c->arrays.color.element(
+                v1->index & vertex_cache_t::INDEX_MASK);
+        c->arrays.color.fetch(c, v1->color.v, cp);
+    }
+    if(!(v2->flags & vertex_t::LIT)) {
+        v2->flags |= vertex_t::LIT;
+        const GLvoid* cp = c->arrays.color.element(
+                v2->index & vertex_cache_t::INDEX_MASK);
+        c->arrays.color.fetch(c, v2->color.v, cp);
+    }
+}
+
+static void lightTriangleDarkFlat(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    if (!(v2->flags & vertex_t::LIT)) {
+        v2->flags |= vertex_t::LIT;
+        const GLvoid* cp = c->arrays.color.element(
+                v2->index & vertex_cache_t::INDEX_MASK);
+        c->arrays.color.fetch(c, v2->color.v, cp);
+    }
+    // configure the rasterizer here, before we clip
+    c->rasterizer.procs.color4xv(c, v2->color.v);
+}
+
+static void lightTriangleSmooth(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    if (!(v0->flags & vertex_t::LIT))
+        c->lighting.lightVertex(c, v0);
+    if (!(v1->flags & vertex_t::LIT))
+        c->lighting.lightVertex(c, v1);
+    if(!(v2->flags & vertex_t::LIT))
+        c->lighting.lightVertex(c, v2);
+}
+
+static void lightTriangleFlat(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    if (!(v2->flags & vertex_t::LIT))
+        c->lighting.lightVertex(c, v2);
+    // configure the rasterizer here, before we clip
+    c->rasterizer.procs.color4xv(c, v2->color.v);
+}
+
+// The fog versions...
+
+static inline
+void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v)
+{
+    if (!(v->flags & vertex_t::LIT)) {
+        v->flags |= vertex_t::LIT;
+        v->fog = c->fog.fog(c, v->window.z);
+        const GLvoid* cp = c->arrays.color.element(
+                v->index & vertex_cache_t::INDEX_MASK);
+        c->arrays.color.fetch(c, v->color.v, cp);
+    }
+}
+static inline
+void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v)
+{
+    if (!(v->flags & vertex_t::LIT)) {
+        v->flags |= vertex_t::LIT;
+        v->fog = c->fog.fog(c, v->window.z);
+    }
+}
+static inline
+void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v)
+{
+    if (!(v->flags & vertex_t::LIT)) {
+        v->fog = c->fog.fog(c, v->window.z);
+        c->lighting.lightVertex(c, v);
+    }
+}
+
+static void lightTriangleDarkSmoothFog(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    lightVertexDarkSmoothFog(c, v0);
+    lightVertexDarkSmoothFog(c, v1);
+    lightVertexDarkSmoothFog(c, v2);
+}
+
+static void lightTriangleDarkFlatFog(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    lightVertexDarkFlatFog(c, v0);
+    lightVertexDarkFlatFog(c, v1);
+    lightVertexDarkSmoothFog(c, v2);
+    // configure the rasterizer here, before we clip
+    c->rasterizer.procs.color4xv(c, v2->color.v);
+}
+
+static void lightTriangleSmoothFog(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    lightVertexSmoothFog(c, v0);
+    lightVertexSmoothFog(c, v1);
+    lightVertexSmoothFog(c, v2);
+}
+
+static void lightTriangleFlatFog(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    lightVertexDarkFlatFog(c, v0);
+    lightVertexDarkFlatFog(c, v1);
+    lightVertexSmoothFog(c, v2);
+    // configure the rasterizer here, before we clip
+    c->rasterizer.procs.color4xv(c, v2->color.v);
+}
+
+
+
+typedef void (*light_primitive_t)(ogles_context_t*,
+        vertex_t*, vertex_t*, vertex_t*);
+
+// fog 0x4, light 0x2, smooth 0x1
+static const light_primitive_t lightPrimitive[8] = {
+    lightTriangleDarkFlat,          // no fog | dark  | flat
+    lightTriangleDarkSmooth,        // no fog | dark  | smooth
+    lightTriangleFlat,              // no fog | light | flat
+    lightTriangleSmooth,            // no fog | light | smooth
+    lightTriangleDarkFlatFog,       // fog    | dark  | flat
+    lightTriangleDarkSmoothFog,     // fog    | dark  | smooth
+    lightTriangleFlatFog,           // fog    | light | flat
+    lightTriangleSmoothFog          // fog    | light | smooth
+};
+
+void ogles_validate_primitives(ogles_context_t* c)
+{
+    const uint32_t enables = c->rasterizer.state.enables;
+
+    // set up the lighting/shading/smoothing/fogging function
+    int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0;
+    index |= c->lighting.enable ? 0x2 : 0;
+    index |= enables & GGL_ENABLE_FOG ? 0x4 : 0;
+    c->lighting.lightTriangle = lightPrimitive[index];
+    
+    // set up the primitive renderers
+    if (ggl_likely(c->arrays.vertex.enable)) {
+        c->prims.renderPoint    = primitive_point;
+        c->prims.renderLine     = primitive_line;
+        c->prims.renderTriangle = primitive_clip_triangle;
+    } else {
+        c->prims.renderPoint    = primitive_nop_point;
+        c->prims.renderLine     = primitive_nop_line;
+        c->prims.renderTriangle = primitive_nop_triangle;
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void compute_iterators_t::initTriangle(
+        vertex_t const* v0, vertex_t const* v1, vertex_t const* v2)
+{
+    m_dx01 = v1->window.x - v0->window.x;
+    m_dy10 = v0->window.y - v1->window.y;
+    m_dx20 = v0->window.x - v2->window.x;
+    m_dy02 = v2->window.y - v0->window.y;
+    m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20;
+}
+
+void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables)
+{
+    m_x0 = v0->window.x;
+    m_y0 = v0->window.y;
+    const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS;
+    const GGLcoord minArea = 2; // cannot be inversed
+    // triangles with an area smaller than 1.0 are not smooth-shaded
+
+    int q=0, s=0, d=0;
+    if (abs(area) >= minArea) {
+        // Here we do some voodoo magic, to compute a suitable scale
+        // factor for deltas/area:
+
+        // First compute the 1/area with full 32-bits precision,
+        // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent.
+        d = gglRecipQNormalized(area, &q);
+
+        // Then compute the minimum left-shift to not overflow the muls
+        // below. 
+        s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20));
+
+        // We'll keep 16-bits of precision for deltas/area. So we need
+        // to shift everything left an extra 15 bits.
+        s += 15;
+        
+        // make sure all final shifts are not > 32, because gglMulx
+        // can't handle it.
+        if (s < q) s = q;
+        if (s > 32) {
+            d >>= 32-s;
+            s = 32;
+        }
+    }
+
+    m_dx01 = gglMulx(m_dx01, d, s);
+    m_dy10 = gglMulx(m_dy10, d, s);
+    m_dx20 = gglMulx(m_dx20, d, s);
+    m_dy02 = gglMulx(m_dy02, d, s);
+    m_area_scale = 32 + q - s;
+    m_scale = 0;
+
+    if (enables & GGL_ENABLE_TMUS) {
+        const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20));
+        const int B = gglClz(abs(m_x0)|abs(m_y0));
+        m_scale = max(0, 32 - (A + 16)) +
+                  max(0, 32 - (B + TRI_FRACTION_BITS)) + 1;
+    }
+}
+
+int compute_iterators_t::iteratorsScale(GGLfixed* it,
+        int32_t c0, int32_t c1, int32_t c2) const
+{
+    int32_t dc01 = c1 - c0;
+    int32_t dc02 = c2 - c0;
+    const int A = gglClz(abs(c0));
+    const int B = gglClz(abs(dc01)|abs(dc02));
+    const int scale = min(A, B - m_scale) - 2;
+    if (scale >= 0) {
+        c0   <<= scale;
+        dc01 <<= scale;
+        dc02 <<= scale;
+    } else {
+        c0   >>= -scale;
+        dc01 >>= -scale;
+        dc02 >>= -scale;
+    }
+    const int s = m_area_scale;
+    int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s);
+    int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s);
+    int32_t c = c0 - (gglMulAddx(dcdx, m_x0, 
+            gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS));
+    it[0] = c;
+    it[1] = dcdx;
+    it[2] = dcdy;
+    return scale;
+}
+
+void compute_iterators_t::iterators1616(GGLfixed* it,
+        GGLfixed c0, GGLfixed c1, GGLfixed c2) const
+{
+    const GGLfixed dc01 = c1 - c0;
+    const GGLfixed dc02 = c2 - c0;
+    // 16.16 x 16.16 == 32.32 --> 16.16
+    const int s = m_area_scale;
+    int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s);
+    int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s);
+    int32_t c = c0 - (gglMulAddx(dcdx, m_x0,
+            gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS));
+    it[0] = c;
+    it[1] = dcdx;
+    it[2] = dcdy;
+}
+
+#if defined(__arm__) && !defined(__thumb__)
+inline void compute_iterators_t::iterators0032(int32_t* it,
+        int32_t c0, int32_t c1, int32_t c2) const
+{
+    ::iterators0032(this, it, c0, c1, c2);
+}
+#else
+void compute_iterators_t::iterators0032(int32_t* it,
+        int32_t c0, int32_t c1, int32_t c2) const
+{
+    const int s = m_area_scale - 16;
+    int32_t dc01 = (c1 - c0)>>s;
+    int32_t dc02 = (c2 - c0)>>s;
+    // 16.16 x 16.16 == 32.32
+    int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10);
+    int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20);
+    int32_t c = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4);
+    it[ 0] = c;
+    it[ 1] = dcdx;
+    it[ 2] = dcdy;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+
+static inline int32_t clampZ(GLfixed z) CONST;
+int32_t clampZ(GLfixed z) {
+    z = (z & ~(z>>31));
+    if (z >= 0x10000)
+        z = 0xFFFF;
+    return z;
+}
+
+static __attribute__((noinline))
+void fetch_texcoord_impl(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    vertex_t* const vtx[3] = { v0, v1, v2 };
+    array_t const * const texcoordArray = c->arrays.texture;
+    
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        if (!(c->rasterizer.state.texture[i].enable))
+            continue;
+        
+        for (int j=0 ; j<3 ; j++) {
+            vertex_t* const v = vtx[j];
+            if (v->flags & vertex_t::TT)
+                continue;
+
+            // NOTE: here we could compute automatic texgen
+            // such as sphere/cube maps, instead of fetching them
+            // from the textcoord array.
+
+            vec4_t& coords = v->texture[i];
+            const GLubyte* tp = texcoordArray[i].element(
+                    v->index & vertex_cache_t::INDEX_MASK);
+            texcoordArray[i].fetch(c, coords.v, tp);
+
+            // transform texture coordinates...
+            coords.Q = 0x10000;
+            const transform_t& tr = c->transforms.texture[i].transform; 
+            if (ggl_unlikely(tr.ops)) {
+                c->arrays.tex_transform[i](&tr, &coords, &coords);
+            }
+
+            // divide by Q
+            const GGLfixed q = coords.Q;
+            if (ggl_unlikely(q != 0x10000)) {
+                const int32_t qinv = gglRecip28(q);
+                coords.S = gglMulx(coords.S, qinv, 28);
+                coords.T = gglMulx(coords.T, qinv, 28);
+            }
+        }
+    }
+    v0->flags |= vertex_t::TT;
+    v1->flags |= vertex_t::TT;
+    v2->flags |= vertex_t::TT;
+}
+
+inline void fetch_texcoord(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    const uint32_t enables = c->rasterizer.state.enables;
+    if (!(enables & GGL_ENABLE_TMUS))
+        return;
+
+    // Fetch & transform texture coordinates...
+    if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) {
+        // already done for all three vertices, bail...
+        return;
+    }
+    fetch_texcoord_impl(c, v0, v1, v2);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Point
+#endif
+
+void primitive_nop_point(ogles_context_t*, vertex_t*) {
+}
+
+void primitive_point(ogles_context_t* c, vertex_t* v)
+{
+    // lighting & clamping...
+    const uint32_t enables = c->rasterizer.state.enables;
+
+    if (ggl_unlikely(!(v->flags & vertex_t::LIT))) {
+        if (c->lighting.enable) {
+            c->lighting.lightVertex(c, v);
+        } else {
+            v->flags |= vertex_t::LIT;
+            const GLvoid* cp = c->arrays.color.element(
+                    v->index & vertex_cache_t::INDEX_MASK);
+            c->arrays.color.fetch(c, v->color.v, cp);
+        }
+        if (enables & GGL_ENABLE_FOG) {
+            v->fog = c->fog.fog(c, v->window.z);
+        }
+    }
+
+    // XXX: we don't need to do that each-time
+    // if color array and lighting not enabled 
+    c->rasterizer.procs.color4xv(c, v->color.v);
+
+    // XXX: look into ES point-sprite extension
+    if (enables & GGL_ENABLE_TMUS) {
+        fetch_texcoord(c, v,v,v);
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+            if (!c->rasterizer.state.texture[i].enable) 
+                continue;
+            int32_t itt[8];
+            itt[1] = itt[2] = itt[4] = itt[5] = 0;
+            itt[6] = itt[7] = 16; // XXX: check that
+            if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) {
+                int width = c->textures.tmu[i].texture->surface.width;
+                itt[0] = v->texture[i].S * width;
+                itt[6] = 0;
+            }
+            if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) {
+                int height = c->textures.tmu[i].texture->surface.height;
+                itt[3] = v->texture[i].T * height;
+                itt[7] = 0;
+            }
+            c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
+        }
+    }
+    
+    if (enables & GGL_ENABLE_DEPTH_TEST) {
+        int32_t itz[3];
+        itz[0] = clampZ(v->window.z) * 0x00010001;
+        itz[1] = itz[2] = 0;
+        c->rasterizer.procs.zGrad3xv(c, itz);
+    }
+
+    if (enables & GGL_ENABLE_FOG) {
+        GLfixed itf[3];
+        itf[0] = v->fog;
+        itf[1] = itf[2] = 0;
+        c->rasterizer.procs.fogGrad3xv(c, itf);
+    }
+
+    // Render our point...
+    c->rasterizer.procs.pointx(c, v->window.v, c->point.size);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Line
+#endif
+
+void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) {
+}
+
+void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1)
+{
+    // This is a cheezy implementation of line drawing that
+    // uses 2 triangles per line. 
+    // That said, how often line drawing is used?
+
+    // get texture coordinates
+    fetch_texcoord(c, v0, v1, v1);
+
+    // light/shade the vertices first (they're copied below)
+    c->lighting.lightTriangle(c, v0, v1, v1);
+
+    vertex_t v[4];
+    v[0] = *v0;
+    v[1] = *v1;
+    v0 = &v[0];
+    v1 = &v[1];
+
+    // clip the line if needed
+    if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) {
+        unsigned int count = clip_line(c, v0, v1);
+        if (ggl_unlikely(count == 0))
+            return;
+    }
+
+    // compute iterators...
+    const uint32_t enables = c->rasterizer.state.enables;
+    const uint32_t mask =   GGL_ENABLE_TMUS |
+                            GGL_ENABLE_SMOOTH |
+                            GGL_ENABLE_W | 
+                            GGL_ENABLE_FOG |
+                            GGL_ENABLE_DEPTH_TEST;
+
+    if (ggl_unlikely(enables & mask)) {
+        c->lerp.initTriangle(v0, v1, v1);
+        lerp_triangle(c, v0, v1, v1);
+    }
+
+    // render our line
+    c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Triangle
+#endif
+
+void primitive_nop_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2) {
+}
+
+void primitive_clip_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL;
+    if (ggl_likely(!cc)) {
+        // code below must be as optimized as possible, this is the
+        // common code path.
+
+        // This triangle is not clipped, test if it's culled
+        // unclipped triangle...
+        c->lerp.initTriangle(v0, v1, v2);
+        if (cull_triangle(c, v0, v1, v2))
+            return; // culled!
+
+        // Fetch all texture coordinates if needed
+        fetch_texcoord(c, v0, v1, v2);
+
+        // light (or shade) our triangle!
+        c->lighting.lightTriangle(c, v0, v1, v2);
+
+        triangle(c, v0, v1, v2);
+        return;
+    }
+
+    // The assumption here is that we're not going to clip very often,
+    // and even more rarely will we clip a triangle that ends up
+    // being culled out. So it's okay to light the vertices here, even though
+    // in a few cases we won't render the triangle (if culled).
+
+    // Fetch texture coordinates...
+    fetch_texcoord(c, v0, v1, v2);
+
+    // light (or shade) our triangle!
+    c->lighting.lightTriangle(c, v0, v1, v2);
+
+    clip_triangle(c, v0, v1, v2);
+}
+
+// -----------------------------------------------------------------------
+
+void triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    // compute iterators...
+    const uint32_t enables = c->rasterizer.state.enables;
+    const uint32_t mask =   GGL_ENABLE_TMUS |
+                            GGL_ENABLE_SMOOTH |
+                            GGL_ENABLE_W | 
+                            GGL_ENABLE_FOG |
+                            GGL_ENABLE_DEPTH_TEST;
+
+    if (ggl_likely(enables & mask))
+        lerp_triangle(c, v0, v1, v2);
+
+    c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v);
+}
+
+void lerp_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    const uint32_t enables = c->rasterizer.state.enables;
+    c->lerp.initLerp(v0, enables);
+
+    // set up texture iterators
+    if (enables & GGL_ENABLE_TMUS) {
+        if (enables & GGL_ENABLE_W) {
+            lerp_texcoords_w(c, v0, v1, v2);
+        } else {
+            lerp_texcoords(c, v0, v1, v2);
+        }
+    }
+
+    // set up the color iterators
+    const compute_iterators_t& lerp = c->lerp;
+    if (enables & GGL_ENABLE_SMOOTH) {
+        GLfixed itc[12];
+        for (int i=0 ; i<4 ; i++) {
+            const GGLcolor c0 = v0->color.v[i] * 255;
+            const GGLcolor c1 = v1->color.v[i] * 255;
+            const GGLcolor c2 = v2->color.v[i] * 255;
+            lerp.iterators1616(&itc[i*3], c0, c1, c2);
+        }
+        c->rasterizer.procs.colorGrad12xv(c, itc);
+    }
+
+    if (enables & GGL_ENABLE_DEPTH_TEST) {
+        int32_t itz[3];
+        const int32_t v0z = clampZ(v0->window.z);
+        const int32_t v1z = clampZ(v1->window.z);
+        const int32_t v2z = clampZ(v2->window.z);
+        lerp.iterators0032(itz, v0z, v1z, v2z);
+        if (ggl_unlikely(c->polygonOffset.enable)) {
+            const GLfixed factor = c->polygonOffset.factor;
+            const GLfixed units = c->polygonOffset.units;
+            int32_t maxDepthSlope = max(abs(itz[1]), abs(itz[2]));
+            int32_t offset = (int64_t(maxDepthSlope)*factor +
+                    (int64_t(units) << 16)) >> 16;
+            itz[0] += offset; // XXX: this can cause overflows
+        }
+        c->rasterizer.procs.zGrad3xv(c, itz);
+    }
+
+    if (ggl_unlikely(enables & GGL_ENABLE_FOG)) {
+        GLfixed itf[3];
+        lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog);
+        c->rasterizer.procs.fogGrad3xv(c, itf);
+    }
+}
+
+
+static inline
+int compute_lod(ogles_context_t* c, int i,
+        int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2)
+{
+    // Compute mipmap level / primitive
+    // rho = sqrt( texelArea / area )
+    // lod = log2( rho )
+    // lod = log2( texelArea / area ) / 2
+    // lod = (log2( texelArea ) - log2( area )) / 2
+    const compute_iterators_t& lerp = c->lerp;
+    const GGLcoord area = abs(lerp.area());
+    const int w = c->textures.tmu[i].texture->surface.width;
+    const int h = c->textures.tmu[i].texture->surface.height;
+    const int shift = 16 + (16 - TRI_FRACTION_BITS);
+    int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) -
+            gglMulx(s2-s0, t1-t0, shift) )*w*h;
+    int log2TArea = (32-TRI_FRACTION_BITS  -1) - gglClz(texelArea);
+    int log2Area  = (32-TRI_FRACTION_BITS*2-1) - gglClz(area);
+    int lod = (log2TArea - log2Area + 1) >> 1;
+    return lod;
+}
+
+void lerp_texcoords(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    const compute_iterators_t& lerp = c->lerp;
+    int32_t itt[8] __attribute__((aligned(16)));
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        const texture_t& tmu = c->rasterizer.state.texture[i];
+        if (!tmu.enable) 
+            continue;
+
+        // compute the jacobians using block floating-point
+        int32_t s0 = v0->texture[i].S;
+        int32_t t0 = v0->texture[i].T;
+        int32_t s1 = v1->texture[i].S;
+        int32_t t1 = v1->texture[i].T;
+        int32_t s2 = v2->texture[i].S;
+        int32_t t2 = v2->texture[i].T;
+
+        const GLenum min_filter = c->textures.tmu[i].texture->min_filter;
+        if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) {
+            int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2);
+            c->rasterizer.procs.bindTextureLod(c, i,
+                    &c->textures.tmu[i].texture->mip(lod));
+        }
+
+        // premultiply (s,t) when clampling
+        if (tmu.s_wrap == GGL_CLAMP) {
+            const int width = tmu.surface.width;
+            s0 *= width;
+            s1 *= width;
+            s2 *= width;
+        }
+        if (tmu.t_wrap == GGL_CLAMP) {
+            const int height = tmu.surface.height;
+            t0 *= height;
+            t1 *= height;
+            t2 *= height;
+        }
+        itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2);
+        itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2);
+        c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
+    }
+}
+
+void lerp_texcoords_w(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    const compute_iterators_t& lerp = c->lerp;
+    int32_t itt[8] __attribute__((aligned(16)));
+    int32_t itw[3];
+
+    // compute W's scale to 2.30
+    int32_t w0 = v0->window.w;
+    int32_t w1 = v1->window.w;
+    int32_t w2 = v2->window.w;
+    int wscale = 32 - gglClz(w0|w1|w2);
+
+    // compute the jacobian using block floating-point    
+    int sc = lerp.iteratorsScale(itw, w0, w1, w2);
+    sc +=  wscale - 16;
+    c->rasterizer.procs.wGrad3xv(c, itw);
+
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        const texture_t& tmu = c->rasterizer.state.texture[i];
+        if (!tmu.enable) 
+            continue;
+
+        // compute the jacobians using block floating-point
+        int32_t s0 = v0->texture[i].S;
+        int32_t t0 = v0->texture[i].T;
+        int32_t s1 = v1->texture[i].S;
+        int32_t t1 = v1->texture[i].T;
+        int32_t s2 = v2->texture[i].S;
+        int32_t t2 = v2->texture[i].T;
+
+        const GLenum min_filter = c->textures.tmu[i].texture->min_filter;
+        if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) {
+            int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2);
+            c->rasterizer.procs.bindTextureLod(c, i,
+                    &c->textures.tmu[i].texture->mip(lod));
+        }
+
+        // premultiply (s,t) when clampling
+        if (tmu.s_wrap == GGL_CLAMP) {
+            const int width = tmu.surface.width;
+            s0 *= width;
+            s1 *= width;
+            s2 *= width;
+        }
+        if (tmu.t_wrap == GGL_CLAMP) {
+            const int height = tmu.surface.height;
+            t0 *= height;
+            t1 *= height;
+            t2 *= height;
+        }
+
+        s0 = gglMulx(s0, w0, wscale);
+        t0 = gglMulx(t0, w0, wscale);
+        s1 = gglMulx(s1, w1, wscale);
+        t1 = gglMulx(t1, w1, wscale);
+        s2 = gglMulx(s2, w2, wscale);
+        t2 = gglMulx(t2, w2, wscale);
+
+        itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2);
+        itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2);
+        c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
+    }
+}
+
+
+static inline
+bool cull_triangle(ogles_context_t* c, vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    if (ggl_likely(c->cull.enable)) {
+        const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW;
+        const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK;
+        if (face == c->cull.cullFace)
+            return true; // culled!
+    }
+    return false;
+}
+
+static inline
+GLfixed frustumPlaneDist(int plane, const vec4_t& s)
+{
+    const GLfixed d = s.v[ plane >> 1 ];
+    return  ((plane & 1) ? (s.w - d) : (s.w + d)); 
+}
+
+static inline
+int32_t clipDivide(GLfixed a, GLfixed b) {
+    // returns a 4.28 fixed-point
+    return gglMulDivi(1LU<<28, a, b);
+} 
+
+void clip_triangle(ogles_context_t* c,
+        vertex_t* v0, vertex_t* v1, vertex_t* v2)
+{
+    uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL;
+
+    vertex_t *p0, *p1, *p2;
+    const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES;
+    const int MAX_VERTICES = 3;
+
+    // Temporary buffer to hold the new vertices. Each plane can add up to 
+    // two new vertices (because the polygon is convex).
+    // We need one extra element, to handle an overflow case when
+    // the polygon degenerates into something non convex.
+    vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1];   // ~3KB
+    vertex_t* buf = buffer;
+
+    // original list of vertices (polygon to clip, in fact this
+    // function works with an arbitrary polygon).
+    vertex_t* in[3] = { v0, v1, v2 };
+    
+    // output lists (we need 2, which we use back and forth)
+    // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES)
+    // 2 more elements for overflow when non convex polygons.
+    vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2];
+    unsigned int outi = 0;
+    
+    // current input list
+    vertex_t** ivl = in;
+
+    // 3 input vertices, 0 in the output list, first plane
+    unsigned int ic = 3;
+
+    // User clip-planes first, the clipping is always done in eye-coordinate
+    // this is basically the same algorithm than for the view-volume
+    // clipping, except for the computation of the distance (vertex, plane)
+    // and the fact that we need to compute the eye-coordinates of each
+    // new vertex we create.
+    
+    if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL))
+    {
+        unsigned int plane = 0;
+        uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8;
+        do {
+            if (cc & 1) {        
+                // pointers to our output list (head and current)
+                vertex_t** const ovl = &out[outi][0];
+                vertex_t** output = ovl;
+                unsigned int oc = 0;
+                unsigned int sentinel = 0;
+                // previous vertice, compute distance to the plane
+                vertex_t* s = ivl[ic-1];
+                const vec4_t& equation = c->clipPlanes.plane[plane].equation;
+                GLfixed sd = dot4(equation.v, s->eye.v);
+                // clip each vertice against this plane...
+                for (unsigned int i=0 ; i<ic ; i++) {            
+                    vertex_t* p = ivl[i];
+                    const GLfixed pd = dot4(equation.v, p->eye.v);
+                    if (sd >= 0) {
+                        if (pd >= 0) {
+                            // both inside
+                            *output++ = p;
+                            oc++;
+                        } else {
+                            // s inside, p outside (exiting)
+                            const GLfixed t = clipDivide(sd, sd-pd);
+                            c->arrays.clipEye(c, buf, t, p, s);
+                            *output++ = buf++;
+                            oc++;
+                            if (++sentinel >= 3)
+                                return; // non-convex polygon!
+                        }
+                    } else {
+                        if (pd >= 0) {
+                            // s outside (entering)
+                            if (pd) {
+                                const GLfixed t = clipDivide(pd, pd-sd);
+                                c->arrays.clipEye(c, buf, t, s, p);
+                                *output++ = buf++;
+                                oc++;
+                                if (++sentinel >= 3)
+                                    return; // non-convex polygon!
+                            }
+                            *output++ = p;
+                            oc++;
+                        } else {
+                           // both outside
+                        }
+                    }
+                    s = p;
+                    sd = pd;
+                }
+                // output list become the new input list
+                if (oc<3)
+                    return; // less than 3 vertices left? we're done!
+                ivl = ovl;
+                ic = oc;
+                outi = 1-outi;
+            }
+            cc >>= 1;
+            plane++;
+        } while (cc);
+    }
+
+    // frustum clip-planes
+    if (all_cc & vertex_t::FRUSTUM_CLIP_ALL)
+    {
+        unsigned int plane = 0;
+        uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL;
+        do {
+            if (cc & 1) {        
+                // pointers to our output list (head and current)
+                vertex_t** const ovl = &out[outi][0];
+                vertex_t** output = ovl;
+                unsigned int oc = 0;
+                unsigned int sentinel = 0;
+                // previous vertice, compute distance to the plane
+                vertex_t* s = ivl[ic-1];
+                GLfixed sd = frustumPlaneDist(plane, s->clip);
+                // clip each vertice against this plane...
+                for (unsigned int i=0 ; i<ic ; i++) {            
+                    vertex_t* p = ivl[i];
+                    const GLfixed pd = frustumPlaneDist(plane, p->clip);
+                    if (sd >= 0) {
+                        if (pd >= 0) {
+                            // both inside
+                            *output++ = p;
+                            oc++;
+                        } else {
+                            // s inside, p outside (exiting)
+                            const GLfixed t = clipDivide(sd, sd-pd);
+                            c->arrays.clipVertex(c, buf, t, p, s);
+                            *output++ = buf++;
+                            oc++;
+                            if (++sentinel >= 3)
+                                return; // non-convex polygon!
+                        }
+                    } else {
+                        if (pd >= 0) {
+                            // s outside (entering)
+                            if (pd) {
+                                const GLfixed t = clipDivide(pd, pd-sd);
+                                c->arrays.clipVertex(c, buf, t, s, p);
+                                *output++ = buf++;
+                                oc++;
+                                if (++sentinel >= 3)
+                                    return; // non-convex polygon!
+                            }
+                            *output++ = p;
+                            oc++;
+                        } else {
+                           // both outside
+                        }
+                    }
+                    s = p;
+                    sd = pd;
+                }
+                // output list become the new input list
+                if (oc<3)
+                    return; // less than 3 vertices left? we're done!
+                ivl = ovl;
+                ic = oc;
+                outi = 1-outi;
+            }
+            cc >>= 1;
+            plane++;
+        } while (cc);
+    }
+    
+    // finally we can render our triangles...
+    p0 = ivl[0];
+    p1 = ivl[1];
+    for (unsigned int i=2 ; i<ic ; i++) {
+        p2 = ivl[i];
+        c->lerp.initTriangle(p0, p1, p2);
+        if (cull_triangle(c, p0, p1, p2)) {
+            p1 = p2;
+            continue; // culled!
+        }
+        triangle(c, p0, p1, p2);
+        p1 = p2;
+    }
+}
+
+unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p)
+{
+    const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL;
+
+    if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL))
+    {
+        unsigned int plane = 0;
+        uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8;
+        do {
+            if (cc & 1) {
+                const vec4_t& equation = c->clipPlanes.plane[plane].equation;
+                const GLfixed sd = dot4(equation.v, s->eye.v);
+                const GLfixed pd = dot4(equation.v, p->eye.v);
+                if (sd >= 0) {
+                    if (pd >= 0) {
+                        // both inside
+                    } else {
+                        // s inside, p outside (exiting)
+                        const GLfixed t = clipDivide(sd, sd-pd);
+                        c->arrays.clipEye(c, p, t, p, s);
+                    }
+                } else {
+                    if (pd >= 0) {
+                        // s outside (entering)
+                        if (pd) {
+                            const GLfixed t = clipDivide(pd, pd-sd);
+                            c->arrays.clipEye(c, s, t, s, p);
+                        }
+                    } else {
+                       // both outside
+                       return 0;
+                    }
+                }
+            }
+            cc >>= 1;
+            plane++;
+        } while (cc);
+    }
+
+    // frustum clip-planes
+    if (all_cc & vertex_t::FRUSTUM_CLIP_ALL)
+    {
+        unsigned int plane = 0;
+        uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL;
+        do {
+            if (cc & 1) {
+                const GLfixed sd = frustumPlaneDist(plane, s->clip);
+                const GLfixed pd = frustumPlaneDist(plane, p->clip);
+                if (sd >= 0) {
+                    if (pd >= 0) {
+                        // both inside
+                    } else {
+                        // s inside, p outside (exiting)
+                        const GLfixed t = clipDivide(sd, sd-pd);
+                        c->arrays.clipVertex(c, p, t, p, s);
+                    }
+                } else {
+                    if (pd >= 0) {
+                        // s outside (entering)
+                        if (pd) {
+                            const GLfixed t = clipDivide(pd, pd-sd);
+                            c->arrays.clipVertex(c, s, t, s, p);
+                        }
+                    } else {
+                       // both outside
+                       return 0;
+                    }
+                }
+            }
+            cc >>= 1;
+            plane++;
+        } while (cc);
+    }
+
+    return 2;
+}
+
+
+}; // namespace android
diff --git a/opengl/libagl/primitives.h b/opengl/libagl/primitives.h
new file mode 100644
index 0000000..1bef604
--- /dev/null
+++ b/opengl/libagl/primitives.h
@@ -0,0 +1,37 @@
+/* libs/opengles/primitives.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_PRIMITIVES_H
+#define ANDROID_OPENGLES_PRIMITIVES_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+
+namespace android {
+
+namespace gl {
+struct ogles_context_t;
+};
+
+void ogles_validate_primitives(ogles_context_t* c);
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_PRIMITIVES_H
+
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
new file mode 100644
index 0000000..c2f9f12
--- /dev/null
+++ b/opengl/libagl/state.cpp
@@ -0,0 +1,592 @@
+/* libs/opengles/state.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdlib.h>
+
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "array.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "light.h"
+#include "texture.h"
+#include "BufferObjectManager.h"
+#include "TextureObjectManager.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static char const * const gVendorString     = "Android";
+static char const * const gRendererString   = "Android PixelFlinger 1.0";
+static char const * const gVersionString    = "OpenGL ES-CM 1.0";
+static char const * const gExtensionsString =
+    "GL_OES_byte_coordinates "              // OK
+    "GL_OES_fixed_point "                   // OK
+    "GL_OES_single_precision "              // OK
+    "GL_OES_read_format "                   // OK
+    "GL_OES_compressed_paletted_texture "   // OK
+    "GL_OES_draw_texture "                  // OK
+    "GL_OES_matrix_get "                    // OK
+    "GL_OES_query_matrix "                  // OK
+    //        "GL_OES_point_size_array "              // TODO
+    //        "GL_OES_point_sprite "                  // TODO
+    "GL_ARB_texture_compression "           // OK
+    "GL_ARB_texture_non_power_of_two "      // OK
+    "GL_ANDROID_direct_texture "            // OK
+    "GL_ANDROID_user_clip_plane "           // OK
+    "GL_ANDROID_vertex_buffer_object "      // OK
+    "GL_ANDROID_generate_mipmap "           // OK
+    ;
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+ogles_context_t *ogles_init(size_t extra)
+{
+    void* const base = malloc(extra + sizeof(ogles_context_t) + 32);
+	if (!base) return 0;
+
+    ogles_context_t *c =
+            (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL);
+    memset(c, 0, sizeof(ogles_context_t));
+    ggl_init_context(&(c->rasterizer));
+    
+    // XXX: this should be passed as an argument
+    sp<EGLSurfaceManager> smgr(new EGLSurfaceManager());
+    c->surfaceManager = smgr.get();
+    c->surfaceManager->incStrong(c);
+
+    sp<EGLBufferObjectManager> bomgr(new EGLBufferObjectManager());
+    c->bufferObjectManager = bomgr.get();
+    c->bufferObjectManager->incStrong(c);
+
+    ogles_init_array(c);
+    ogles_init_matrix(c);
+    ogles_init_vertex(c);
+    ogles_init_light(c);
+    ogles_init_texture(c);
+
+    c->rasterizer.base = base;
+    c->point.size = TRI_ONE;
+    c->line.width = TRI_ONE;
+            
+    // in OpenGL, writing to the depth buffer is enabled by default.
+    c->rasterizer.procs.depthMask(c, 1);
+    
+    // OpenGL enables dithering by default
+    c->rasterizer.procs.enable(c, GL_DITHER);
+
+    return c;
+}
+
+void ogles_uninit(ogles_context_t* c)
+{
+    ogles_uninit_array(c);
+    ogles_uninit_matrix(c);
+    ogles_uninit_vertex(c);
+    ogles_uninit_light(c);
+    ogles_uninit_texture(c);
+    c->surfaceManager->decStrong(c);
+    c->bufferObjectManager->decStrong(c);
+    ggl_uninit_context(&(c->rasterizer));
+	free(c->rasterizer.base);
+}
+
+void _ogles_error(ogles_context_t* c, GLenum error)
+{
+    if (c->error == GL_NO_ERROR)
+        c->error = error;
+}
+
+static bool stencilop_valid(GLenum op) {
+    switch (op) {
+    case GL_KEEP:
+    case GL_ZERO:
+    case GL_REPLACE:
+    case GL_INCR:
+    case GL_DECR:
+    case GL_INVERT:
+        return true;
+    }
+    return false;
+}
+
+static void enable_disable(ogles_context_t* c, GLenum cap, int enabled)
+{
+    if ((cap >= GL_LIGHT0) && (cap<GL_LIGHT0+OGLES_MAX_LIGHTS)) {
+        c->lighting.lights[cap-GL_LIGHT0].enable = enabled;
+        c->lighting.enabledLights &= ~(1<<(cap-GL_LIGHT0));
+        c->lighting.enabledLights |= (enabled<<(cap-GL_LIGHT0));
+        return;
+    }
+
+    switch (cap) {
+    case GL_POINT_SMOOTH:
+        c->point.smooth = enabled;
+        break;
+    case GL_LINE_SMOOTH:
+        c->line.smooth = enabled;
+        break;
+    case GL_POLYGON_OFFSET_FILL:
+        c->polygonOffset.enable = enabled;
+        break;
+    case GL_CULL_FACE:
+        c->cull.enable = enabled;
+        break;
+    case GL_LIGHTING:
+        c->lighting.enable = enabled;
+        break;
+    case GL_COLOR_MATERIAL:
+        c->lighting.colorMaterial.enable = enabled;
+        break;
+    case GL_NORMALIZE:
+    case GL_RESCALE_NORMAL:
+        c->transforms.rescaleNormals = enabled ? cap : 0;
+        // XXX: invalidate mvit
+        break;
+
+    case GL_CLIP_PLANE0:
+    case GL_CLIP_PLANE1:
+    case GL_CLIP_PLANE2:
+    case GL_CLIP_PLANE3:
+    case GL_CLIP_PLANE4:
+    case GL_CLIP_PLANE5:
+        c->clipPlanes.enable &= ~(1<<(cap-GL_CLIP_PLANE0));
+        c->clipPlanes.enable |= (enabled<<(cap-GL_CLIP_PLANE0));
+        ogles_invalidate_perspective(c);
+        break;
+
+    case GL_FOG:
+    case GL_DEPTH_TEST:
+        ogles_invalidate_perspective(c);
+        // fall-through...
+    case GL_BLEND:
+    case GL_SCISSOR_TEST:
+    case GL_ALPHA_TEST:
+    case GL_COLOR_LOGIC_OP:
+    case GL_DITHER:
+    case GL_STENCIL_TEST:
+    case GL_TEXTURE_2D:
+        // these need to fall through into the rasterizer
+        c->rasterizer.procs.enableDisable(c, cap, enabled);
+        break;
+        
+    case GL_MULTISAMPLE:
+    case GL_SAMPLE_ALPHA_TO_COVERAGE:
+    case GL_SAMPLE_ALPHA_TO_ONE:
+    case GL_SAMPLE_COVERAGE:
+        // not supported in this implementation
+        break;
+
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+using namespace android;
+
+#if 0
+#pragma mark -
+#endif
+
+// These ones are super-easy, we're not supporting those features!
+void glSampleCoverage(GLclampf value, GLboolean invert) {
+}
+void glSampleCoveragex(GLclampx value, GLboolean invert) {
+}
+void glStencilFunc(GLenum func, GLint ref, GLuint mask) {
+    ogles_context_t* c = ogles_context_t::get();
+    if (func < GL_NEVER || func > GL_ALWAYS) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    // from OpenGL|ES 1.0 sepcification:
+    // If there is no stencil buffer, no stencil modification can occur
+    // and it is as if the stencil test always passes.
+}
+
+void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
+    ogles_context_t* c = ogles_context_t::get();
+    if ((stencilop_valid(fail) &
+         stencilop_valid(zfail) &
+         stencilop_valid(zpass)) == 0) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void glAlphaFunc(GLenum func, GLclampf ref)
+{
+    glAlphaFuncx(func, gglFloatToFixed(ref));
+}
+
+void glCullFace(GLenum mode)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    switch (mode) {
+    case GL_FRONT:
+    case GL_BACK:
+    case GL_FRONT_AND_BACK:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+    }
+    c->cull.cullFace = mode;
+}
+
+void glFrontFace(GLenum mode)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    switch (mode) {
+    case GL_CW:
+    case GL_CCW:
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->cull.frontFace = mode;
+}
+
+void glHint(GLenum target, GLenum mode)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    switch (target) {
+    case GL_FOG_HINT:
+    case GL_GENERATE_MIPMAP_HINT:
+    case GL_LINE_SMOOTH_HINT:
+        break;
+    case GL_POINT_SMOOTH_HINT:
+        c->rasterizer.procs.enableDisable(c, 
+                GGL_POINT_SMOOTH_NICE, mode==GL_NICEST);
+        break;
+    case GL_PERSPECTIVE_CORRECTION_HINT:
+        c->perspective = (mode == GL_NICEST) ? 1 : 0;
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+    }
+}
+
+void glEnable(GLenum cap) {
+    ogles_context_t* c = ogles_context_t::get();
+    enable_disable(c, cap, 1);
+}
+void glDisable(GLenum cap) {
+    ogles_context_t* c = ogles_context_t::get();
+    enable_disable(c, cap, 0);
+}
+
+void glFinish()
+{ // nothing to do for our software implementation
+}
+
+void glFlush()
+{ // nothing to do for our software implementation
+}
+
+GLenum glGetError()
+{
+    // From OpenGL|ES 1.0 specification:
+    // If more than one flag has recorded an error, glGetError returns
+    // and clears an arbitrary error flag value. Thus, glGetError should
+    // always be called in a loop, until it returns GL_NO_ERROR,
+    // if all error flags are to be reset.
+
+    ogles_context_t* c = ogles_context_t::get();
+    if (c->error) {
+        const GLenum ret(c->error);
+        c->error = 0;
+        return ret;
+    }
+    
+    if (c->rasterizer.error) {
+        const GLenum ret(c->rasterizer.error);
+        c->rasterizer.error = 0;
+        return ret;
+    }
+
+    return GL_NO_ERROR;
+}
+
+const GLubyte* glGetString(GLenum string)
+{
+    switch (string) {
+    case GL_VENDOR:     return (const GLubyte*)gVendorString;
+    case GL_RENDERER:   return (const GLubyte*)gRendererString;
+    case GL_VERSION:    return (const GLubyte*)gVersionString;
+    case GL_EXTENSIONS: return (const GLubyte*)gExtensionsString;
+    }
+    ogles_context_t* c = ogles_context_t::get();
+    ogles_error(c, GL_INVALID_ENUM);
+    return 0;
+}
+
+void glGetIntegerv(GLenum pname, GLint *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    switch (pname) {
+    case GL_ALIASED_POINT_SIZE_RANGE:
+        params[0] = 0;
+        params[1] = GGL_MAX_ALIASED_POINT_SIZE;
+        break;
+    case GL_ALIASED_LINE_WIDTH_RANGE:
+        params[0] = 0;
+        params[1] = GGL_MAX_ALIASED_POINT_SIZE;
+        break;
+    case GL_ALPHA_BITS: {
+        int index = c->rasterizer.state.buffers.color.format;
+        GGLFormat const * formats = gglGetPixelFormatTable();
+        params[0] = formats[index].ah - formats[index].al;
+        break; 
+        }
+    case GL_RED_BITS: {
+        int index = c->rasterizer.state.buffers.color.format;
+        GGLFormat const * formats = gglGetPixelFormatTable();
+        params[0] = formats[index].rh - formats[index].rl;
+        break; 
+        }
+    case GL_GREEN_BITS: {
+        int index = c->rasterizer.state.buffers.color.format;
+        GGLFormat const * formats = gglGetPixelFormatTable();
+        params[0] = formats[index].gh - formats[index].gl;
+        break; 
+        }
+    case GL_BLUE_BITS: {
+        int index = c->rasterizer.state.buffers.color.format;
+        GGLFormat const * formats = gglGetPixelFormatTable();
+        params[0] = formats[index].bh - formats[index].bl;
+        break; 
+        }
+    case GL_COMPRESSED_TEXTURE_FORMATS:
+        params[ 0] = GL_PALETTE4_RGB8_OES;
+        params[ 1] = GL_PALETTE4_RGBA8_OES;
+        params[ 2] = GL_PALETTE4_R5_G6_B5_OES;
+        params[ 3] = GL_PALETTE4_RGBA4_OES;
+        params[ 4] = GL_PALETTE4_RGB5_A1_OES;
+        params[ 5] = GL_PALETTE8_RGB8_OES;
+        params[ 6] = GL_PALETTE8_RGBA8_OES;
+        params[ 7] = GL_PALETTE8_R5_G6_B5_OES;
+        params[ 8] = GL_PALETTE8_RGBA4_OES;
+        params[ 9] = GL_PALETTE8_RGB5_A1_OES;
+        break;
+    case GL_DEPTH_BITS:
+        params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16;
+        break;
+    case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
+        params[0] = GL_RGB;
+        break;
+    case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
+        params[0] = GL_UNSIGNED_SHORT_5_6_5;
+        break;
+    case GL_MAX_ELEMENTS_INDICES:
+        params[0] = 65536;
+        break;
+    case GL_MAX_ELEMENTS_VERTICES:
+        params[0] = 0x7FFFFFFF;
+        break;
+    case GL_MAX_LIGHTS:
+        params[0] = OGLES_MAX_LIGHTS;
+        break;
+    case GL_MAX_CLIP_PLANES:
+        params[0] = OGLES_MAX_CLIP_PLANES;
+        break;
+    case GL_MAX_MODELVIEW_STACK_DEPTH:
+        params[0] = OGLES_MODELVIEW_STACK_DEPTH;
+        break;
+    case GL_MAX_PROJECTION_STACK_DEPTH:
+        params[0] = OGLES_PROJECTION_STACK_DEPTH;
+        break;
+    case GL_MAX_TEXTURE_STACK_DEPTH:
+        params[0] = OGLES_TEXTURE_STACK_DEPTH;
+        break;
+    case GL_MAX_TEXTURE_SIZE:
+        params[0] = GGL_MAX_TEXTURE_SIZE;
+        break;
+    case GL_MAX_TEXTURE_UNITS:
+        params[0] = GGL_TEXTURE_UNIT_COUNT;
+        break;
+    case GL_MAX_VIEWPORT_DIMS:
+        params[0] = GGL_MAX_VIEWPORT_DIMS;
+        params[1] = GGL_MAX_VIEWPORT_DIMS;
+        break;
+    case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
+        params[0] = OGLES_NUM_COMPRESSED_TEXTURE_FORMATS;
+        break;
+    case GL_SMOOTH_LINE_WIDTH_RANGE:
+        params[0] = 0;
+        params[1] = GGL_MAX_SMOOTH_LINE_WIDTH;
+        break;
+    case GL_SMOOTH_POINT_SIZE_RANGE:
+        params[0] = 0;
+        params[1] = GGL_MAX_SMOOTH_POINT_SIZE;
+        break;
+    case GL_STENCIL_BITS:
+        params[0] = 0;
+        break;
+    case GL_SUBPIXEL_BITS:
+        params[0] = GGL_SUBPIXEL_BITS;
+        break;
+
+    case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES:
+        memcpy( params,
+                c->transforms.modelview.top().elements(),
+                16*sizeof(GLint));
+        break;
+    case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES:
+        memcpy( params,
+                c->transforms.projection.top().elements(),
+                16*sizeof(GLint));
+        break;
+    case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES:
+        memcpy( params,
+                c->transforms.texture[c->textures.active].top().elements(),
+                16*sizeof(GLint));
+        break;
+
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        break;
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void glPointSize(GLfloat size)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (size <= 0) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->point.size = TRI_FROM_FIXED(gglFloatToFixed(size));
+}
+
+void glPointSizex(GLfixed size)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (size <= 0) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->point.size = TRI_FROM_FIXED(size);
+}
+
+// ----------------------------------------------------------------------------
+
+void glLineWidth(GLfloat width)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (width <= 0) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->line.width = TRI_FROM_FIXED(gglFloatToFixed(width));
+}
+
+void glLineWidthx(GLfixed width)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (width <= 0) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->line.width = TRI_FROM_FIXED(width);
+}
+
+// ----------------------------------------------------------------------------
+
+void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.colorMask(c, r, g, b, a);
+}
+
+void glDepthMask(GLboolean flag) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.depthMask(c, flag);
+}
+
+void glStencilMask(GLuint mask) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.stencilMask(c, mask);
+}
+
+void glDepthFunc(GLenum func) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.depthFunc(c, func);
+}
+
+void glLogicOp(GLenum opcode) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.logicOp(c, opcode);
+}
+
+void glAlphaFuncx(GLenum func, GLclampx ref) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.alphaFuncx(c, func, ref);
+}
+
+void glBlendFunc(GLenum sfactor, GLenum dfactor) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.blendFunc(c, sfactor, dfactor);
+}
+
+void glClear(GLbitfield mask) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clear(c, mask);
+}
+
+void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clearColorx(c, red, green, blue, alpha);
+}
+
+void glClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clearColorx(c,
+                    gglFloatToFixed(r),
+                    gglFloatToFixed(g),
+                    gglFloatToFixed(b),
+                    gglFloatToFixed(a));
+}
+
+void glClearDepthx(GLclampx depth) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clearDepthx(c, depth);
+}
+
+void glClearDepthf(GLclampf depth)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clearDepthx(c, gglFloatToFixed(depth));
+}
+
+void glClearStencil(GLint s) {
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.clearStencil(c, s);
+}
diff --git a/opengl/libagl/state.h b/opengl/libagl/state.h
new file mode 100644
index 0000000..55a5ccb
--- /dev/null
+++ b/opengl/libagl/state.h
@@ -0,0 +1,54 @@
+/* libs/opengles/state.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_STATE_H
+#define ANDROID_OPENGLES_STATE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include <GLES/gl.h>
+
+#include <stdio.h>
+
+namespace android {
+
+ogles_context_t *ogles_init(size_t extra);
+void ogles_uninit(ogles_context_t* c);
+void _ogles_error(ogles_context_t* c, GLenum error);
+
+#ifndef TRACE_GL_ERRORS
+#define TRACE_GL_ERRORS 0
+#endif
+
+#if TRACE_GL_ERRORS
+#define ogles_error(c, error) \
+do { \
+  printf("ogles_error at file %s line %d\n", __FILE__, __LINE__); \
+  _ogles_error(c, error); \
+} while (0)
+#else /* !TRACE_GL_ERRORS */
+#define ogles_error(c, error) _ogles_error((c), (error))
+#endif
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_STATE_H
+
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
new file mode 100644
index 0000000..6b2640a
--- /dev/null
+++ b/opengl/libagl/texture.cpp
@@ -0,0 +1,1423 @@
+/* libs/opengles/texture.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "texture.h"
+#include "TextureObjectManager.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static void bindTextureTmu(
+    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex);
+
+static __attribute__((noinline))
+void generateMipmap(ogles_context_t* c, GLint level);
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Init
+#endif
+
+void ogles_init_texture(ogles_context_t* c)
+{
+    c->textures.packAlignment   = 4;
+    c->textures.unpackAlignment = 4;
+
+    // each context has a default named (0) texture (not shared)
+    c->textures.defaultTexture = new EGLTextureObject();
+    c->textures.defaultTexture->incStrong(c);
+    
+    // bind the default texture to each texture unit
+    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        bindTextureTmu(c, i, 0, c->textures.defaultTexture);
+        memset(c->current.texture[i].v, 0, sizeof(vec4_t));
+        c->current.texture[i].Q = 0x10000;
+    }
+}
+
+void ogles_uninit_texture(ogles_context_t* c)
+{
+    if (c->textures.ggl)
+        gglUninit(c->textures.ggl);
+    c->textures.defaultTexture->decStrong(c);
+    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        if (c->textures.tmu[i].texture)
+            c->textures.tmu[i].texture->decStrong(c);
+    }
+}
+
+static __attribute__((noinline))
+void validate_tmu(ogles_context_t* c, int i)
+{
+    texture_unit_t& u(c->textures.tmu[i]);
+    if (u.dirty) {
+        u.dirty = 0;
+        c->rasterizer.procs.activeTexture(c, i);
+        c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
+        c->rasterizer.procs.texGeni(c, GGL_S,
+                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+        c->rasterizer.procs.texGeni(c, GGL_T,
+                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                GGL_TEXTURE_WRAP_S, u.texture->wraps);
+        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                GGL_TEXTURE_WRAP_T, u.texture->wrapt);
+        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
+        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
+
+        // disable this texture unit if it's not complete
+        if (!u.texture->isComplete()) {
+            c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
+        }
+    }
+}
+
+void ogles_validate_texture_impl(ogles_context_t* c)
+{
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        if (c->rasterizer.state.texture[i].enable)
+            validate_tmu(c, i);
+    }
+    c->rasterizer.procs.activeTexture(c, c->textures.active);
+}
+
+static
+void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
+    c->textures.tmu[tmu].dirty = flags;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Format conversion
+#endif
+
+static uint32_t gl2format_table[6][4] = {
+    // BYTE, 565, 4444, 5551
+    { GGL_PIXEL_FORMAT_A_8,
+      0, 0, 0 },                        // GL_ALPHA
+    { GGL_PIXEL_FORMAT_RGB_888,
+      GGL_PIXEL_FORMAT_RGB_565,
+      0, 0 },                           // GL_RGB
+    { GGL_PIXEL_FORMAT_RGBA_8888,
+      0,
+      GGL_PIXEL_FORMAT_RGBA_4444,
+      GGL_PIXEL_FORMAT_RGBA_5551 },     // GL_RGBA
+    { GGL_PIXEL_FORMAT_L_8,
+      0, 0, 0 },                        // GL_LUMINANCE
+    { GGL_PIXEL_FORMAT_LA_88,
+      0, 0, 0 },                        // GL_LUMINANCE_ALPHA
+};
+
+static int32_t convertGLPixelFormat(GLint format, GLenum type)
+{
+    int32_t fi = -1;
+    int32_t ti = -1;
+    switch (format) {
+    case GL_ALPHA:              fi = 0;     break;
+    case GL_RGB:                fi = 1;     break;
+    case GL_RGBA:               fi = 2;     break;
+    case GL_LUMINANCE:          fi = 3;     break;
+    case GL_LUMINANCE_ALPHA:    fi = 4;     break;
+    }
+    switch (type) {
+    case GL_UNSIGNED_BYTE:          ti = 0; break;
+    case GL_UNSIGNED_SHORT_5_6_5:   ti = 1; break;
+    case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break;
+    case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break;
+    }
+    if (fi==-1 || ti==-1)
+        return 0;
+    return gl2format_table[fi][ti];
+}
+
+// ----------------------------------------------------------------------------
+
+static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type)
+{
+    GLenum error = 0;
+    if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) {
+        error = GL_INVALID_ENUM;
+    }
+    if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 &&
+        type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) {
+        error = GL_INVALID_ENUM;
+    }
+    if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) {
+        error = GL_INVALID_OPERATION;
+    }
+    if ((type == GL_UNSIGNED_SHORT_4_4_4_4 ||
+         type == GL_UNSIGNED_SHORT_5_5_5_1)  && format != GL_RGBA) {
+        error = GL_INVALID_OPERATION;
+    }
+    if (error) {
+        ogles_error(c, error);
+    }
+    return error;
+}
+
+// ----------------------------------------------------------------------------
+
+GGLContext* getRasterizer(ogles_context_t* c)
+{
+    GGLContext* ggl = c->textures.ggl;
+    if (ggl_unlikely(!ggl)) {
+        // this is quite heavy the first time...
+        gglInit(&ggl);
+        if (!ggl) {
+            return 0;
+        }
+        GGLfixed colors[4] = { 0, 0, 0, 0x10000 };
+        c->textures.ggl = ggl;
+        ggl->activeTexture(ggl, 0);
+        ggl->enable(ggl, GGL_TEXTURE_2D);
+        ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+        ggl->disable(ggl, GGL_DITHER);
+        ggl->shadeModel(ggl, GGL_FLAT);
+        ggl->color4xv(ggl, colors);
+    }
+    return ggl;
+}
+
+static __attribute__((noinline))
+int copyPixels(
+        ogles_context_t* c,
+        const GGLSurface& dst,
+        GLint xoffset, GLint yoffset,
+        const GGLSurface& src,
+        GLint x, GLint y, GLsizei w, GLsizei h)
+{
+    if ((dst.format == src.format) &&
+        (dst.stride == src.stride) &&
+        (dst.width == src.width) &&
+        (dst.height == src.height) &&
+        (dst.stride > 0) &&
+        ((x|y) == 0) &&
+        ((xoffset|yoffset) == 0))
+    {
+        // this is a common case...
+        const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]);
+        const size_t size = src.height * src.stride * pixelFormat.size;
+        memcpy(dst.data, src.data, size);
+        return 0;
+    }
+
+    // use pixel-flinger to handle all the conversions
+    GGLContext* ggl = getRasterizer(c);
+    if (!ggl) {
+        // the only reason this would fail is because we ran out of memory
+        return GL_OUT_OF_MEMORY;
+    }
+
+    ggl->colorBuffer(ggl, &dst);
+    ggl->bindTexture(ggl, &src);
+    ggl->texCoord2i(ggl, x-xoffset, y-yoffset);
+    ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h);
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+
+static __attribute__((noinline))
+sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
+{
+    sp<EGLTextureObject> tex;
+    const int active = c->textures.active;
+    const GLuint name = c->textures.tmu[active].name;
+
+    // free the reference to the previously bound object
+    texture_unit_t& u(c->textures.tmu[active]);
+    if (u.texture)
+        u.texture->decStrong(c);
+
+    if (name == 0) {
+        // 0 is our local texture object, not shared with anyone. 
+        // But it affects all bound TMUs immediately.
+        // (we need to invalidate all units bound to this texture object)
+        tex = c->textures.defaultTexture;
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+            if (c->textures.tmu[i].texture == tex.get())
+                invalidate_texture(c, i);
+        }
+    } else {
+        // get a new texture object for that name
+        tex = c->surfaceManager->replaceTexture(name);
+    }
+
+    // bind this texture to the current active texture unit
+    // and add a reference to this texture object
+    u.texture = tex.get();
+    u.texture->incStrong(c);
+    u.name = name;
+    invalidate_texture(c, active);    
+    return tex;
+}
+
+void bindTextureTmu(
+    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
+{
+    if (tex.get() == c->textures.tmu[tmu].texture)
+        return;
+    
+    // free the reference to the previously bound object
+    texture_unit_t& u(c->textures.tmu[tmu]);
+    if (u.texture)
+        u.texture->decStrong(c);
+
+    // bind this texture to the current active texture unit
+    // and add a reference to this texture object
+    u.texture = tex.get();
+    u.texture->incStrong(c);
+    u.name = texture;
+    invalidate_texture(c, tmu);
+}
+
+int createTextureSurface(ogles_context_t* c,
+        GGLSurface** outSurface, int32_t* outSize, GLint level,
+        GLenum format, GLenum type, GLsizei width, GLsizei height,
+        GLenum compressedFormat = 0)
+{
+    // find out which texture is bound to the current unit
+    const int active = c->textures.active;
+    const GLuint name = c->textures.tmu[active].name;
+
+    // convert the pixelformat to one we can handle
+    const int32_t formatIdx = convertGLPixelFormat(format, type);
+    if (formatIdx == 0) { // we don't know what to do with this
+        return GL_INVALID_OPERATION;
+    }
+    
+    // figure out the size we need as well as the stride
+    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
+    const int32_t align = c->textures.unpackAlignment-1;
+    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
+    const size_t size = bpr * height;
+    const int32_t stride = bpr / pixelFormat.size;
+
+    if (level > 0) {
+        const int active = c->textures.active;
+        EGLTextureObject* tex = c->textures.tmu[active].texture;
+        status_t err = tex->reallocate(level,
+                width, height, stride, formatIdx, compressedFormat, bpr);
+        if (err != NO_ERROR)
+            return GL_OUT_OF_MEMORY;
+        GGLSurface& surface = tex->editMip(level);
+        *outSurface = &surface;
+        *outSize = size;
+        return 0;
+    }
+
+    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
+    status_t err = tex->reallocate(level,
+            width, height, stride, formatIdx, compressedFormat, bpr);
+    if (err != NO_ERROR)
+        return GL_OUT_OF_MEMORY;
+
+    tex->internalformat = format;
+    *outSurface = &tex->surface;
+    *outSize = size;
+    return 0;
+}
+
+static void decodePalette4(const GLvoid *data, int level, int width, int height,
+                           void *surface, int stride, int format)
+
+{
+    int indexBits = 8;
+    int entrySize = 0;
+    switch (format) {
+    case GL_PALETTE4_RGB8_OES:
+        indexBits = 4;
+        /* FALLTHROUGH */
+    case GL_PALETTE8_RGB8_OES:
+        entrySize = 3;
+        break;
+
+    case GL_PALETTE4_RGBA8_OES:
+        indexBits = 4;
+        /* FALLTHROUGH */
+    case GL_PALETTE8_RGBA8_OES:
+        entrySize = 4;
+        break;
+
+    case GL_PALETTE4_R5_G6_B5_OES:
+    case GL_PALETTE4_RGBA4_OES:
+    case GL_PALETTE4_RGB5_A1_OES:
+        indexBits = 4;
+        /* FALLTHROUGH */
+    case GL_PALETTE8_R5_G6_B5_OES:
+    case GL_PALETTE8_RGBA4_OES:
+    case GL_PALETTE8_RGB5_A1_OES:
+        entrySize = 2;
+        break;
+    }
+
+    const int paletteSize = (1 << indexBits) * entrySize;
+    uint8_t const* pixels = (uint8_t *)data + paletteSize;
+    for (int i=0 ; i<level ; i++) {
+        int w = (width  >> i) ? : 1;
+        int h = (height >> i) ? : 1;
+        pixels += h * ((w * indexBits) / 8);
+    }
+    width  = (width  >> level) ? : 1;
+    height = (height >> level) ? : 1;
+
+    if (entrySize == 2) {
+        uint8_t const* const palette = (uint8_t*)data;
+        for (int y=0 ; y<height ; y++) {
+            uint8_t* p = (uint8_t*)surface + y*stride*2;
+            if (indexBits == 8) {
+                for (int x=0 ; x<width ; x++) {
+                    int index = 2 * (*pixels++);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                }
+            } else {
+                for (int x=0 ; x<width ; x+=2) {
+                    int v = *pixels++;
+                    int index = 2 * (v >> 4);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                    if (x+1 < width) {
+                        index = 2 * (v & 0xF);
+                        *p++ = palette[index + 0];
+                        *p++ = palette[index + 1];
+                    }
+                }
+            }
+        }
+    } else if (entrySize == 3) {
+        uint8_t const* const palette = (uint8_t*)data;
+        for (int y=0 ; y<height ; y++) {
+            uint8_t* p = (uint8_t*)surface + y*stride*3;
+            if (indexBits == 8) {
+                for (int x=0 ; x<width ; x++) {
+                    int index = 3 * (*pixels++);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                    *p++ = palette[index + 2];
+                }
+            } else {
+                for (int x=0 ; x<width ; x+=2) {
+                    int v = *pixels++;
+                    int index = 3 * (v >> 4);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                    *p++ = palette[index + 2];
+                    if (x+1 < width) {
+                        index = 3 * (v & 0xF);
+                        *p++ = palette[index + 0];
+                        *p++ = palette[index + 1];
+                        *p++ = palette[index + 2];
+                    }
+                }
+            }
+        }
+    } else if (entrySize == 4) {
+        uint8_t const* const palette = (uint8_t*)data;
+        for (int y=0 ; y<height ; y++) {
+            uint8_t* p = (uint8_t*)surface + y*stride*4;
+            if (indexBits == 8) {
+                for (int x=0 ; x<width ; x++) {
+                    int index = 4 * (*pixels++);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                    *p++ = palette[index + 2];
+                    *p++ = palette[index + 3];
+                }
+            } else {
+                for (int x=0 ; x<width ; x+=2) {
+                    int v = *pixels++;
+                    int index = 4 * (v >> 4);
+                    *p++ = palette[index + 0];
+                    *p++ = palette[index + 1];
+                    *p++ = palette[index + 2];
+                    *p++ = palette[index + 3];
+                    if (x+1 < width) {
+                        index = 4 * (v & 0xF);
+                        *p++ = palette[index + 0];
+                        *p++ = palette[index + 1];
+                        *p++ = palette[index + 2];
+                        *p++ = palette[index + 3];
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+
+static __attribute__((noinline))
+void set_depth_and_fog(ogles_context_t* c, GLint z)
+{
+    const uint32_t enables = c->rasterizer.state.enables;
+    // we need to compute Zw
+    int32_t iterators[3];
+    iterators[1] = iterators[2] = 0;
+    GGLfixed Zw;
+    GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear);
+    GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar);
+    if (z<=0)       Zw = n;
+    else if (z>=1)  Zw = f;
+    else            Zw = gglMulAddx(z, (f-n), n);
+    if (enables & GGL_ENABLE_FOG) {
+        // set up fog if needed...
+        iterators[0] = c->fog.fog(c, Zw);
+        c->rasterizer.procs.fogGrad3xv(c, iterators);
+    }
+    if (enables & GGL_ENABLE_DEPTH_TEST) {
+        // set up z-test if needed...
+        int32_t z = (Zw & ~(Zw>>31));
+        if (z >= 0x10000)
+            z = 0xFFFF;
+        iterators[0] = (z << 16) | z;
+        c->rasterizer.procs.zGrad3xv(c, iterators);
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Generate mimaps
+#endif
+
+extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex);
+
+void generateMipmap(ogles_context_t* c, GLint level)
+{
+    if (level == 0) {
+        const int active = c->textures.active;
+        EGLTextureObject* tex = c->textures.tmu[active].texture;
+        if (tex->generate_mipmap) {
+            if (buildAPyramid(c, tex) != NO_ERROR) {
+                ogles_error(c, GL_OUT_OF_MEMORY);
+                return;
+            }
+        }
+    }
+}
+
+
+static void texParameterx(
+        GLenum target, GLenum pname, GLfixed param, ogles_context_t* c)
+{
+    if (target != GGL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    
+    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;    
+    switch (pname) {
+    case GL_TEXTURE_WRAP_S:
+        if ((param == GL_CLAMP) ||
+            (param == GL_REPEAT) ||
+            (param == GL_CLAMP_TO_EDGE)) {
+            textureObject->wraps = param;
+        } else {
+            goto invalid_enum;
+        }
+        break;
+    case GL_TEXTURE_WRAP_T:
+        if ((param == GGL_CLAMP) ||
+            (param == GGL_REPEAT) ||
+            (param == GGL_CLAMP_TO_EDGE)) {
+            textureObject->wrapt = param;
+        } else {
+            goto invalid_enum;
+        }
+        break;
+    case GL_TEXTURE_MIN_FILTER:
+        if ((param == GL_NEAREST) ||
+            (param == GL_LINEAR) ||
+            (param == GL_NEAREST_MIPMAP_NEAREST) ||
+            (param == GL_LINEAR_MIPMAP_NEAREST) ||
+            (param == GL_NEAREST_MIPMAP_LINEAR) ||
+            (param == GL_LINEAR_MIPMAP_LINEAR)) {
+            textureObject->min_filter = param;
+        } else {
+            goto invalid_enum;
+        }
+        break;
+    case GL_TEXTURE_MAG_FILTER:
+        if ((param == GL_NEAREST) ||
+            (param == GL_LINEAR)) {
+            textureObject->mag_filter = param;
+        } else {
+            goto invalid_enum;
+        }
+        break;
+    case GL_GENERATE_MIPMAP:
+        textureObject->generate_mipmap = param;
+        break;
+    default:
+invalid_enum:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    invalidate_texture(c, c->textures.active);
+}
+
+
+static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
+        ogles_context_t* c)
+{
+    // quickly reject empty rects
+    if ((w|h) <= 0)
+        return;                
+
+    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+    y = gglIntToFixed(cbSurface.height) - (y + h);
+    w >>= FIXED_BITS;
+    h >>= FIXED_BITS;
+
+    // set up all texture units
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        if (!c->rasterizer.state.texture[i].enable)
+            continue;
+
+        int32_t texcoords[8];
+        texture_unit_t& u(c->textures.tmu[i]);
+
+        // validate this tmu (bind, wrap, filter)
+        validate_tmu(c, i);
+        // we CLAMP here, which works with premultiplied (s,t)
+        c->rasterizer.procs.texParameteri(c,
+                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
+        c->rasterizer.procs.texParameteri(c,
+                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
+        u.dirty = 0xFF; // XXX: should be more subtle
+
+        EGLTextureObject* textureObject = u.texture;  
+        const GLint Ucr = textureObject->crop_rect[0] << 16;
+        const GLint Vcr = textureObject->crop_rect[1] << 16;
+        const GLint Wcr = textureObject->crop_rect[2] << 16;
+        const GLint Hcr = textureObject->crop_rect[3] << 16;
+
+        // computes texture coordinates (pre-multiplied)
+        int32_t dsdx = Wcr / w;   // dsdx =  ((Wcr/w)/Wt)*Wt
+        int32_t dtdy =-Hcr / h;   // dtdy = -((Hcr/h)/Ht)*Ht
+        int32_t s0   = Ucr       - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
+        int32_t t0   = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy
+        texcoords[0] = s0;
+        texcoords[1] = dsdx;
+        texcoords[2] = 0;
+        texcoords[3] = t0;
+        texcoords[4] = 0;
+        texcoords[5] = dtdy;
+        texcoords[6] = 0;
+        texcoords[7] = 0;
+        c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords);
+    }
+
+    const uint32_t enables = c->rasterizer.state.enables;
+    if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
+        set_depth_and_fog(c, z);
+
+    c->rasterizer.procs.activeTexture(c, c->textures.active);
+    c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
+    c->rasterizer.procs.disable(c, GGL_W_LERP);
+    c->rasterizer.procs.disable(c, GGL_AA);
+    c->rasterizer.procs.shadeModel(c, GL_FLAT);
+    c->rasterizer.procs.recti(c, 
+            gglFixedToIntRound(x),
+            gglFixedToIntRound(y),
+            gglFixedToIntRound(x)+w,
+            gglFixedToIntRound(y)+h);
+}
+
+static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)
+{
+    // All coordinates are integer, so if we have only one
+    // texture unit active and no scaling is required
+    // THEN, we can use our special 1:1 mapping
+    // which is a lot faster.
+
+    if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
+        const int tmu = 0;
+        texture_unit_t& u(c->textures.tmu[tmu]);
+        EGLTextureObject* textureObject = u.texture;  
+        const GLint Wcr = textureObject->crop_rect[2];
+        const GLint Hcr = textureObject->crop_rect[3];
+
+        if ((w == Wcr) && (h == -Hcr)) {
+            if ((w|h) <= 0) return; // quickly reject empty rects
+
+            if (u.dirty) {
+                c->rasterizer.procs.activeTexture(c, tmu);
+                c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
+                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                        GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
+                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
+                        GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
+            }
+            c->rasterizer.procs.texGeni(c, GGL_S,
+                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+            c->rasterizer.procs.texGeni(c, GGL_T,
+                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+            u.dirty = 0xFF; // XXX: should be more subtle
+            c->rasterizer.procs.activeTexture(c, c->textures.active);
+            
+            const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+            y = cbSurface.height - (y + h);
+            const GLint Ucr = textureObject->crop_rect[0];
+            const GLint Vcr = textureObject->crop_rect[1];
+            const GLint s0  = Ucr - x;
+            const GLint t0  = (Vcr + Hcr) - y;
+            
+            const GLuint tw = textureObject->surface.width;
+            const GLuint th = textureObject->surface.height;
+            if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) {
+                // The GL spec is unclear about what should happen
+                // in this case, so we just use the slow case, which
+                // at least won't crash
+                goto slow_case;
+            } 
+
+            c->rasterizer.procs.texCoord2i(c, s0, t0);
+            const uint32_t enables = c->rasterizer.state.enables;
+            if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
+                set_depth_and_fog(c, z);
+
+            c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
+            c->rasterizer.procs.disable(c, GGL_W_LERP);
+            c->rasterizer.procs.disable(c, GGL_AA);
+            c->rasterizer.procs.shadeModel(c, GL_FLAT);
+            c->rasterizer.procs.recti(c, x, y, x+w, y+h);
+            return;
+        }
+    }
+
+slow_case:
+    drawTexxOES(
+            gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z),
+            gglIntToFixed(w), gglIntToFixed(h),
+            c);
+}
+
+
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+
+#if 0
+#pragma mark -
+#pragma mark Texture API
+#endif
+
+void glActiveTexture(GLenum texture)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    c->textures.active = texture - GL_TEXTURE0;
+    c->rasterizer.procs.activeTexture(c, c->textures.active);
+}
+
+void glBindTexture(GLenum target, GLuint texture)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    // Bind or create a texture
+    sp<EGLTextureObject> tex;    
+    if (texture == 0) {
+        // 0 is our local texture object
+        tex = c->textures.defaultTexture;
+    } else {
+        tex = c->surfaceManager->texture(texture);
+        if (ggl_unlikely(tex == 0)) {
+            tex = c->surfaceManager->createTexture(texture);
+            if (tex == 0) {
+                ogles_error(c, GL_OUT_OF_MEMORY);
+                return;
+            }
+        }
+    }
+    bindTextureTmu(c, c->textures.active, texture, tex);
+}
+
+void glGenTextures(GLsizei n, GLuint *textures)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (n<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    // generate unique (shared) texture names
+    c->surfaceManager->getToken(n, textures);
+}
+
+void glDeleteTextures(GLsizei n, const GLuint *textures)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (n<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    // If deleting a bound texture, bind this unit to 0
+    for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
+        if (c->textures.tmu[t].name == 0)
+            continue;
+        for (int i=0 ; i<n ; i++) {
+            if (textures[i] && (textures[i] == c->textures.tmu[t].name)) {
+                // bind this tmu to texture 0
+                sp<EGLTextureObject> tex(c->textures.defaultTexture);
+                bindTextureTmu(c, t, 0, tex);
+            }
+        }
+    }
+    c->surfaceManager->deleteTextures(n, textures);
+    c->surfaceManager->recycleTokens(n, textures);
+}
+
+void glMultiTexCoord4f(
+        GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    const int tmu = target-GL_TEXTURE0;
+    c->current.texture[tmu].S = gglFloatToFixed(s);
+    c->current.texture[tmu].T = gglFloatToFixed(t);
+    c->current.texture[tmu].R = gglFloatToFixed(r);
+    c->current.texture[tmu].Q = gglFloatToFixed(q);
+}
+
+void glMultiTexCoord4x(
+        GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    const int tmu = target-GL_TEXTURE0;
+    c->current.texture[tmu].S = s;
+    c->current.texture[tmu].T = t;
+    c->current.texture[tmu].R = r;
+    c->current.texture[tmu].Q = q;
+}
+
+void glPixelStorei(GLenum pname, GLint param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }    
+    if ((param<=0 || param>8) || (param & (param-1))) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (pname == GL_PACK_ALIGNMENT)
+        c->textures.packAlignment = param;
+    if (pname == GL_UNPACK_ALIGNMENT)
+        c->textures.unpackAlignment = param;
+}
+
+void glTexEnvf(GLenum target, GLenum pname, GLfloat param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.texEnvi(c, target, pname, GLint(param));
+}
+
+void glTexEnvfv(
+        GLenum target, GLenum pname, const GLfloat *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (pname == GL_TEXTURE_ENV_MODE) {
+        c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params));
+        return;
+    }
+    if (pname == GL_TEXTURE_ENV_COLOR) {
+        GGLfixed fixed[4];
+        for (int i=0 ; i<4 ; i++)
+            fixed[i] = gglFloatToFixed(params[i]);
+        c->rasterizer.procs.texEnvxv(c, target, pname, fixed);
+        return;
+    }
+    ogles_error(c, GL_INVALID_ENUM);
+}
+
+void glTexEnvx(GLenum target, GLenum pname, GLfixed param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.texEnvi(c, target, pname, param);
+}
+
+void glTexEnvxv(
+        GLenum target, GLenum pname, const GLfixed *params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->rasterizer.procs.texEnvxv(c, target, pname, params);
+}
+
+void glTexParameteriv(
+        GLenum target, GLenum pname, const GLint* params)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GGL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
+    switch (pname) {
+    case GL_TEXTURE_CROP_RECT_OES:
+        memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+}
+
+void glTexParameterf(
+        GLenum target, GLenum pname, GLfloat param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    texParameterx(target, pname, GLfixed(param), c);
+}
+
+void glTexParameterx(
+        GLenum target, GLenum pname, GLfixed param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    texParameterx(target, pname, param, c);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void glCompressedTexImage2D(
+        GLenum target, GLint level, GLenum internalformat,
+        GLsizei width, GLsizei height, GLint border,
+        GLsizei imageSize, const GLvoid *data)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if ((internalformat < GL_PALETTE4_RGB8_OES ||
+         internalformat > GL_PALETTE8_RGB5_A1_OES)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (width<0 || height<0 || border!=0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    // "uncompress" the texture since pixelflinger doesn't support
+    // any compressed texture format natively. 
+    GLenum format;
+    GLenum type;
+    switch (internalformat) {
+    case GL_PALETTE8_RGB8_OES:
+    case GL_PALETTE4_RGB8_OES:
+        format      = GL_RGB;
+        type        = GL_UNSIGNED_BYTE;
+        break;
+    case GL_PALETTE8_RGBA8_OES:
+    case GL_PALETTE4_RGBA8_OES:
+        format      = GL_RGBA;
+        type        = GL_UNSIGNED_BYTE;
+        break;
+    case GL_PALETTE8_R5_G6_B5_OES:
+    case GL_PALETTE4_R5_G6_B5_OES:
+        format      = GL_RGB;
+        type        = GL_UNSIGNED_SHORT_5_6_5;
+        break;
+    case GL_PALETTE8_RGBA4_OES:
+    case GL_PALETTE4_RGBA4_OES:
+        format      = GL_RGBA;
+        type        = GL_UNSIGNED_SHORT_4_4_4_4;
+        break;
+    case GL_PALETTE8_RGB5_A1_OES:
+    case GL_PALETTE4_RGB5_A1_OES:
+        format      = GL_RGBA;
+        type        = GL_UNSIGNED_SHORT_5_5_5_1;
+        break;
+    default:
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    if (!data || !width || !height) {
+        // unclear if this is an error or not...
+        return;
+    }
+
+    int32_t size;
+    GGLSurface* surface;
+    // all mipmap levels are specified at once.
+    const int numLevels = level<0 ? -level : 1;
+    for (int i=0 ; i<numLevels ; i++) {
+        int lod_w = (width  >> i) ? : 1;
+        int lod_h = (height >> i) ? : 1;
+        int error = createTextureSurface(c, &surface, &size,
+                i, format, type, lod_w, lod_h);
+        if (error) {
+            ogles_error(c, error);
+            return;
+        }
+        decodePalette4(data, i, width, height,
+                surface->data, surface->stride, internalformat);
+    }
+}
+
+
+void glTexImage2D(
+        GLenum target, GLint level, GLenum internalformat,
+        GLsizei width, GLsizei height, GLint border,
+        GLenum format, GLenum type, const GLvoid *pixels)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (width<0 || height<0 || border!=0 || level < 0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (format != internalformat) {
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+    if (validFormatType(c, format, type)) {
+        return;
+    }
+
+    int32_t size = 0;
+    GGLSurface* surface = 0;
+    if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
+        int error = createTextureSurface(c, &surface, &size,
+                level, format, type, width, height);
+        if (error) {
+            ogles_error(c, error);
+            return;
+        }
+    } else if (pixels == 0 || level != 0) {
+        // pixel can't be null for direct texture
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+
+    if (pixels) {
+        const int32_t formatIdx = convertGLPixelFormat(format, type);
+        const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
+        const int32_t align = c->textures.unpackAlignment-1;
+        const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
+        const size_t size = bpr * height;
+        const int32_t stride = bpr / pixelFormat.size;
+
+        GGLSurface userSurface;
+        userSurface.version = sizeof(userSurface);
+        userSurface.width  = width;
+        userSurface.height = height;
+        userSurface.stride = stride;
+        userSurface.format = formatIdx;
+        userSurface.compressedFormat = 0;
+        userSurface.data = (GLubyte*)pixels;
+
+        if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
+            int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); 
+            if (err) {
+                ogles_error(c, err);
+                return;
+            }
+            generateMipmap(c, level);
+        } else {
+            // bind it to the texture unit
+            sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
+            tex->setSurface(&userSurface);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void glCompressedTexSubImage2D(
+        GLenum target, GLint level, GLint xoffset,
+        GLint yoffset, GLsizei width, GLsizei height,
+        GLenum format, GLsizei imageSize,
+        const GLvoid *data)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    ogles_error(c, GL_INVALID_ENUM);
+}
+
+void glTexSubImage2D(
+        GLenum target, GLint level, GLint xoffset,
+        GLint yoffset, GLsizei width, GLsizei height,
+        GLenum format, GLenum type, const GLvoid *pixels)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (validFormatType(c, format, type)) {
+        return;
+    }
+
+    // find out which texture is bound to the current unit
+    const int active = c->textures.active;
+    EGLTextureObject* tex = c->textures.tmu[active].texture;
+    const GGLSurface& surface(tex->mip(level));
+
+    if (!tex->internalformat || tex->direct) {
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+    if ((xoffset + width  > GLsizei(surface.width)) ||
+        (yoffset + height > GLsizei(surface.height))) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (!width || !height) {
+        return; // okay, but no-op.
+    }
+
+    // figure out the size we need as well as the stride
+    const int32_t formatIdx = convertGLPixelFormat(format, type);
+    if (formatIdx == 0) { // we don't know what to do with this
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+
+    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
+    const int32_t align = c->textures.unpackAlignment-1;
+    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
+    const size_t size = bpr * height;
+    const int32_t stride = bpr / pixelFormat.size;
+    GGLSurface userSurface;
+    userSurface.version = sizeof(userSurface);
+    userSurface.width  = width;
+    userSurface.height = height;
+    userSurface.stride = stride;
+    userSurface.format = formatIdx;
+    userSurface.compressedFormat = 0;
+    userSurface.data = (GLubyte*)pixels;
+
+    int err = copyPixels(c,
+            surface, xoffset, yoffset,
+            userSurface, 0, 0, width, height); 
+    if (err) {
+        ogles_error(c, err);
+        return;
+    }
+
+    generateMipmap(c, level);
+
+    // since we only changed the content of the texture, we don't need
+    // to call bindTexture on the main rasterizer.
+}
+
+// ----------------------------------------------------------------------------
+
+void glCopyTexImage2D(
+        GLenum target, GLint level, GLenum internalformat,
+        GLint x, GLint y, GLsizei width, GLsizei height,
+        GLint border)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (width<0 || height<0 || border!=0 || level<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    GLenum format = 0;
+    GLenum type = GL_UNSIGNED_BYTE;
+    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+    const int cbFormatIdx = cbSurface.format;
+    switch (cbFormatIdx) {
+    case GGL_PIXEL_FORMAT_RGB_565:
+        type = GL_UNSIGNED_SHORT_5_6_5;
+        break;
+    case GGL_PIXEL_FORMAT_RGBA_5551:
+        type = GL_UNSIGNED_SHORT_5_5_5_1;
+        break;
+    case GGL_PIXEL_FORMAT_RGBA_4444:
+        type = GL_UNSIGNED_SHORT_4_4_4_4;
+        break;
+    }
+    switch (internalformat) {
+    case GL_ALPHA:
+    case GL_LUMINANCE_ALPHA:
+    case GL_LUMINANCE:
+        type = GL_UNSIGNED_BYTE;
+        break;    
+    }
+
+    // figure out the format to use for the new texture
+    switch (cbFormatIdx) {
+    case GGL_PIXEL_FORMAT_RGBA_8888:
+    case GGL_PIXEL_FORMAT_A_8:
+    case GGL_PIXEL_FORMAT_RGBA_5551:
+    case GGL_PIXEL_FORMAT_RGBA_4444:
+        format = internalformat;
+        break;    
+    case GGL_PIXEL_FORMAT_RGBX_8888:
+    case GGL_PIXEL_FORMAT_RGB_888:
+    case GGL_PIXEL_FORMAT_RGB_565:
+    case GGL_PIXEL_FORMAT_L_8:
+        switch (internalformat) {
+        case GL_LUMINANCE:
+        case GL_RGB:
+            format = internalformat;
+            break;    
+        }
+        break;
+    }
+
+    if (format == 0) {
+        // invalid combination
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    // create the new texture...
+    int32_t size;
+    GGLSurface* surface;
+    int error = createTextureSurface(c, &surface, &size,
+            level, format, type, width, height);
+    if (error) {
+        ogles_error(c, error);
+        return;
+    }
+    
+    // The bottom row is stored first in textures
+    GGLSurface txSurface(*surface);
+    txSurface.stride = -txSurface.stride;
+
+    // (x,y) is the lower-left corner of colorBuffer
+    y = cbSurface.height - (y + height);
+
+    int err = copyPixels(c,
+            txSurface, 0, 0,
+            cbSurface, x, y, cbSurface.width, cbSurface.height);  
+    if (err) {
+        ogles_error(c, err);
+    }
+
+    generateMipmap(c, level);
+}
+
+void glCopyTexSubImage2D(
+        GLenum target, GLint level, GLint xoffset, GLint yoffset,
+        GLint x, GLint y, GLsizei width, GLsizei height)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if (target != GL_TEXTURE_2D) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (!width || !height) {
+        return; // okay, but no-op.
+    }
+
+    // find out which texture is bound to the current unit
+    const int active = c->textures.active;
+    EGLTextureObject* tex = c->textures.tmu[active].texture;
+    const GGLSurface& surface(tex->mip(level));
+
+    if (!tex->internalformat) {
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+    if ((xoffset + width  > GLsizei(surface.width)) ||
+        (yoffset + height > GLsizei(surface.height))) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    // The bottom row is stored first in textures
+    GGLSurface txSurface(surface);
+    txSurface.stride = -txSurface.stride;
+
+    // (x,y) is the lower-left corner of colorBuffer
+    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+    y = cbSurface.height - (y + height);
+
+    int err = copyPixels(c,
+            surface, xoffset, yoffset,
+            cbSurface, x, y, width, height);  
+    if (err) {
+        ogles_error(c, err);
+        return;
+    }
+
+    generateMipmap(c, level);
+}
+
+void glReadPixels(
+        GLint x, GLint y, GLsizei width, GLsizei height,
+        GLenum format, GLenum type, GLvoid *pixels)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    if ((format != GL_RGBA) && (format != GL_RGB)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+    if (width<0 || height<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+    if (x<0 || x<0) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    int32_t formatIdx = GGL_PIXEL_FORMAT_NONE;
+    if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) {
+        formatIdx = GGL_PIXEL_FORMAT_RGBA_8888;
+    } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) {
+        formatIdx = GGL_PIXEL_FORMAT_RGB_565;
+    } else {
+        ogles_error(c, GL_INVALID_OPERATION);
+        return;
+    }
+
+    const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s;
+    if ((x+width > GLint(readSurface.width)) ||
+            (y+height > GLint(readSurface.height))) {
+        ogles_error(c, GL_INVALID_VALUE);
+        return;
+    }
+
+    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
+    const int32_t align = c->textures.packAlignment-1;
+    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
+    const int32_t stride = bpr / pixelFormat.size;
+
+    GGLSurface userSurface;
+    userSurface.version = sizeof(userSurface);
+    userSurface.width  = width;
+    userSurface.height = height;
+    userSurface.stride = -stride; // bottom row is transfered first
+    userSurface.format = formatIdx;
+    userSurface.compressedFormat = 0;
+    userSurface.data = (GLubyte*)pixels;
+
+    // use pixel-flinger to handle all the conversions
+    GGLContext* ggl = getRasterizer(c);
+    if (!ggl) {
+        // the only reason this would fail is because we ran out of memory
+        ogles_error(c, GL_OUT_OF_MEMORY);
+        return;
+    }
+
+    ggl->colorBuffer(ggl, &userSurface);  // destination is user buffer 
+    ggl->bindTexture(ggl, &readSurface);  // source is read-buffer
+    ggl->texCoord2i(ggl, x, readSurface.height - (y + height));
+    ggl->recti(ggl, 0, 0, width, height);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark DrawTexture Extension
+#endif
+
+void glDrawTexsvOES(const GLshort* coords) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
+}
+void glDrawTexivOES(const GLint* coords) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
+}
+void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexiOES(x, y, z, w, h, c);
+}
+void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexiOES(x, y, z, w, h, c);
+}
+
+void glDrawTexfvOES(const GLfloat* coords) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexxOES(
+            gglFloatToFixed(coords[0]),
+            gglFloatToFixed(coords[1]),
+            gglFloatToFixed(coords[2]),
+            gglFloatToFixed(coords[3]),
+            gglFloatToFixed(coords[4]),
+            c);
+}
+void glDrawTexxvOES(const GLfixed* coords) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
+}
+void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexxOES(
+            gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z),
+            gglFloatToFixed(w), gglFloatToFixed(h),
+            c);
+}
+void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
+    ogles_context_t* c = ogles_context_t::get();
+    drawTexxOES(x, y, z, w, h, c);
+}
diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h
new file mode 100644
index 0000000..5c57948
--- /dev/null
+++ b/opengl/libagl/texture.h
@@ -0,0 +1,45 @@
+/* libs/opengles/texture.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_TEXTURE_H
+#define ANDROID_OPENGLES_TEXTURE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include <GLES/gl.h>
+
+#include "context.h"
+
+namespace android {
+
+void ogles_init_texture(ogles_context_t* c);
+void ogles_uninit_texture(ogles_context_t* c);
+void ogles_validate_texture_impl(ogles_context_t* c);
+
+inline void ogles_validate_texture(ogles_context_t* c) {
+    if (c->rasterizer.state.enables & GGL_ENABLE_TMUS)
+        ogles_validate_texture_impl(c);
+}
+
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_TEXTURE_H
diff --git a/opengl/libagl/vertex.cpp b/opengl/libagl/vertex.cpp
new file mode 100644
index 0000000..5bcc9dc
--- /dev/null
+++ b/opengl/libagl/vertex.cpp
@@ -0,0 +1,247 @@
+/* libs/opengles/vertex.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "context.h"
+#include "fp.h"
+#include "vertex.h"
+#include "state.h"
+#include "matrix.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void ogles_init_vertex(ogles_context_t* c)
+{
+    c->cull.enable = GL_FALSE;
+    c->cull.cullFace = GL_BACK;
+    c->cull.frontFace = GL_CCW;
+
+    c->current.color.r = 0x10000;
+    c->current.color.g = 0x10000;
+    c->current.color.b = 0x10000;
+    c->current.color.a = 0x10000;
+
+    c->currentNormal.z = 0x10000;
+}
+
+void ogles_uninit_vertex(ogles_context_t* c)
+{
+}
+
+// ----------------------------------------------------------------------------
+// vertex processing
+// ----------------------------------------------------------------------------
+
+// Divides a vertex clip coordinates by W
+static inline
+void perspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
+{
+    // [x,y,z]window = vpt * ([x,y,z]clip / clip.w)
+    // [w]window = 1/w
+
+    // With a regular projection generated by glFrustum(),
+    // we have w=-z, therefore, w is in [zNear, zFar].
+    // Also, zNear and zFar are stricly positive,
+    // and 1/w (window.w) is in [1/zFar, 1/zNear], usually this
+    // means ]0, +inf[ -- however, it is always recommended
+    // to use as large values as possible for zNear.
+    // All in all, w is usually smaller than 1.0 (assuming
+    // zNear is at least 1.0); and even if zNear is smaller than 1.0
+    // values of w won't be too big.
+
+    const int32_t rw = gglRecip28(v->clip.w);
+    const GLfixed* const m = c->transforms.vpt.transform.matrix.m;
+    v->window.w = rw;
+    v->window.x = gglMulAddx(gglMulx(v->clip.x, rw, 16), m[ 0], m[12], 28); 
+    v->window.y = gglMulAddx(gglMulx(v->clip.y, rw, 16), m[ 5], m[13], 28);
+    v->window.x = TRI_FROM_FIXED(v->window.x);
+    v->window.y = TRI_FROM_FIXED(v->window.y);
+    if (enables & GGL_ENABLE_DEPTH_TEST) {
+        v->window.z = gglMulAddx(gglMulx(v->clip.z, rw, 16), m[10], m[14], 28);
+    }
+}
+
+// frustum clipping and W-divide
+static inline
+void clipFrustumPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
+{
+    // ndc = clip / W
+    // window = ncd * viewport
+    
+    // clip to the view-volume
+    uint32_t clip = v->flags & vertex_t::CLIP_ALL;
+    const GLfixed w = v->clip.w;
+    if (v->clip.x < -w)   clip |= vertex_t::CLIP_L;
+    if (v->clip.x >  w)   clip |= vertex_t::CLIP_R;
+    if (v->clip.y < -w)   clip |= vertex_t::CLIP_B;
+    if (v->clip.y >  w)   clip |= vertex_t::CLIP_T;
+    if (v->clip.z < -w)   clip |= vertex_t::CLIP_N;
+    if (v->clip.z >  w)   clip |= vertex_t::CLIP_F;
+
+    v->flags |= clip;
+    c->arrays.cull &= clip;
+
+    if (ggl_likely(!clip)) {
+        // if the vertice is clipped, we don't do the perspective
+        // divide, since we don't need its window coordinates.
+        perspective(c, v, enables);
+    }
+}
+
+// frustum clipping, user clipping and W-divide
+static inline
+void clipAllPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
+{
+    // compute eye coordinates
+    c->arrays.mv_transform(
+            &c->transforms.modelview.transform, &v->eye, &v->obj);
+    v->flags |= vertex_t::EYE;
+
+    // clip this vertex against each user clip plane
+    uint32_t clip = 0;
+    int planes = c->clipPlanes.enable;
+    while (planes) {
+        const int i = 31 - gglClz(planes);
+        planes &= ~(1<<i);
+        // XXX: we should have a special dot() for 2,3,4 coords vertices
+        GLfixed d = dot4(c->clipPlanes.plane[i].equation.v, v->eye.v);
+        if (d < 0) {
+            clip |= 0x100<<i;
+        }
+    }
+    v->flags |= clip;
+
+    clipFrustumPerspective(c, v, enables);
+}
+
+// ----------------------------------------------------------------------------
+
+void ogles_vertex_project(ogles_context_t* c, vertex_t* v) {
+    perspective(c, v, c->rasterizer.state.enables);
+}
+
+void ogles_vertex_perspective2D(ogles_context_t* c, vertex_t* v)
+{
+    // here we assume w=1.0 and the viewport transformation
+    // has been applied already.
+    c->arrays.cull = 0;
+    v->window.x = TRI_FROM_FIXED(v->clip.x);
+    v->window.y = TRI_FROM_FIXED(v->clip.y);
+    v->window.z = v->clip.z;
+    v->window.w = v->clip.w << 12;
+}
+
+void ogles_vertex_perspective3DZ(ogles_context_t* c, vertex_t* v) {
+    clipFrustumPerspective(c, v, GGL_ENABLE_DEPTH_TEST);
+}
+void ogles_vertex_perspective3D(ogles_context_t* c, vertex_t* v) {
+    clipFrustumPerspective(c, v, 0);
+}
+void ogles_vertex_clipAllPerspective3DZ(ogles_context_t* c, vertex_t* v) {
+    clipAllPerspective(c, v, GGL_ENABLE_DEPTH_TEST);
+}
+void ogles_vertex_clipAllPerspective3D(ogles_context_t* c, vertex_t* v) {
+    clipAllPerspective(c, v, 0);
+}
+
+static void clipPlanex(GLenum plane, const GLfixed* equ, ogles_context_t* c)
+{
+    const int p = plane - GL_CLIP_PLANE0;
+    if (ggl_unlikely(uint32_t(p) > (GL_CLIP_PLANE5 - GL_CLIP_PLANE0))) {
+        ogles_error(c, GL_INVALID_ENUM);
+        return;
+    }
+
+    vec4_t& equation = c->clipPlanes.plane[p].equation;
+    memcpy(equation.v, equ, sizeof(vec4_t));
+
+    ogles_validate_transform(c, transform_state_t::MVIT);
+    transform_t& mvit = c->transforms.mvit4;
+    mvit.point4(&mvit, &equation, &equation);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+
+void glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->current.color.r       = gglFloatToFixed(r);
+    c->currentColorClamped.r = gglClampx(c->current.color.r);
+    c->current.color.g       = gglFloatToFixed(g);
+    c->currentColorClamped.g = gglClampx(c->current.color.g);
+    c->current.color.b       = gglFloatToFixed(b);
+    c->currentColorClamped.b = gglClampx(c->current.color.b);
+    c->current.color.a       = gglFloatToFixed(a);
+    c->currentColorClamped.a = gglClampx(c->current.color.a);
+}
+
+void glColor4x(GLfixed r, GLfixed g, GLfixed b, GLfixed a)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->current.color.r = r;
+    c->current.color.g = g;
+    c->current.color.b = b;
+    c->current.color.a = a;
+    c->currentColorClamped.r = gglClampx(r);
+    c->currentColorClamped.g = gglClampx(g);
+    c->currentColorClamped.b = gglClampx(b);
+    c->currentColorClamped.a = gglClampx(a);
+}
+
+void glNormal3f(GLfloat x, GLfloat y, GLfloat z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->currentNormal.x = gglFloatToFixed(x);
+    c->currentNormal.y = gglFloatToFixed(y);
+    c->currentNormal.z = gglFloatToFixed(z);
+}
+
+void glNormal3x(GLfixed x, GLfixed y, GLfixed z)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    c->currentNormal.x = x;
+    c->currentNormal.y = y;
+    c->currentNormal.z = z;
+}
+
+// ----------------------------------------------------------------------------
+
+void glClipPlanef(GLenum plane, const GLfloat* equ)
+{
+    const GLfixed equx[4] = {
+            gglFloatToFixed(equ[0]),
+            gglFloatToFixed(equ[1]),
+            gglFloatToFixed(equ[2]),
+            gglFloatToFixed(equ[3])
+    };
+    ogles_context_t* c = ogles_context_t::get();
+    clipPlanex(plane, equx, c);
+}
+
+void glClipPlanex(GLenum plane, const GLfixed* equ)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    clipPlanex(plane, equ, c);
+}
diff --git a/opengl/libagl/vertex.h b/opengl/libagl/vertex.h
new file mode 100644
index 0000000..55e6213
--- /dev/null
+++ b/opengl/libagl/vertex.h
@@ -0,0 +1,48 @@
+/* libs/opengles/vertex.h
+**
+** Copyright 2006, 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.
+*/
+
+#ifndef ANDROID_OPENGLES_VERTEX_H
+#define ANDROID_OPENGLES_VERTEX_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace gl {
+struct vertex_t;
+struct ogles_context_t;
+};
+
+void ogles_init_vertex(ogles_context_t* c);
+void ogles_uninit_vertex(ogles_context_t* c);
+
+void ogles_vertex_perspective2D(ogles_context_t*, vertex_t*);
+
+void ogles_vertex_perspective3D(ogles_context_t*, vertex_t*);
+void ogles_vertex_perspective3DZ(ogles_context_t*, vertex_t*);
+void ogles_vertex_clipAllPerspective3D(ogles_context_t*, vertex_t*);
+void ogles_vertex_clipAllPerspective3DZ(ogles_context_t*, vertex_t*);
+
+
+void ogles_vertex_project(ogles_context_t* c, vertex_t*);
+
+}; // namespace android
+
+#endif // ANDROID_OPENGLES_VERTEX_H
+