Begin gl2 support.  Renderscript still uses GL1.1 by default.  However, 2.0 can be enabled and will render most tests correctly.
diff --git a/Android.mk b/Android.mk
index 262ac8d..3080ab0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -96,11 +96,14 @@
 	rsScript.cpp \
 	rsScriptC.cpp \
 	rsScriptC_Lib.cpp \
+        rsShaderCache.cpp \
 	rsSimpleMesh.cpp \
 	rsThreadIO.cpp \
-	rsType.cpp
+	rsType.cpp \
+	rsVertexArray.cpp
 
-LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libacc
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libRS
 LOCAL_MODULE_TAGS := optional
diff --git a/java/Fountain/src/com/android/fountain/FountainView.java b/java/Fountain/src/com/android/fountain/FountainView.java
index 1e7c5a2..fcb93f4 100644
--- a/java/Fountain/src/com/android/fountain/FountainView.java
+++ b/java/Fountain/src/com/android/fountain/FountainView.java
@@ -51,7 +51,7 @@
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
         super.surfaceChanged(holder, format, w, h);
         if (mRS == null) {
-            mRS = createRenderScript(false, true);
+            mRS = createRenderScript(false);
             mRS.contextSetSurface(w, h, holder.getSurface());
             mRender = new FountainRS();
             mRender.init(mRS, getResources(), w, h);
diff --git a/rsContext.cpp b/rsContext.cpp
index c1943c0..427a6cc 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -50,11 +50,12 @@
     }
 }
 
-void Context::initEGL()
+void Context::initEGL(bool useGL2)
 {
     mEGL.mNumConfigs = -1;
     EGLint configAttribs[128];
     EGLint *configAttribsPtr = configAttribs;
+    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
 
     memset(configAttribs, 0, sizeof(configAttribs));
 
@@ -62,6 +63,12 @@
     configAttribsPtr[1] = EGL_WINDOW_BIT;
     configAttribsPtr += 2;
 
+    if (useGL2) {
+        configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
+        configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
+        configAttribsPtr += 2;
+    }
+
     if (mUseDepth) {
         configAttribsPtr[0] = EGL_DEPTH_SIZE;
         configAttribsPtr[1] = 16;
@@ -91,7 +98,11 @@
     //eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs);
 
 
-    mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, NULL);
+    if (useGL2) {
+        mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2);
+    } else {
+        mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, NULL);
+    }
     checkEglError("eglCreateContext");
     if (mEGL.mContext == EGL_NO_CONTEXT) {
         LOGE("eglCreateContext returned EGL_NO_CONTEXT");
@@ -223,10 +234,20 @@
 
 void Context::setupCheck()
 {
-    mFragmentStore->setupGL(this, &mStateFragmentStore);
-    mFragment->setupGL(this, &mStateFragment);
-    mRaster->setupGL(this, &mStateRaster);
-    mVertex->setupGL(this, &mStateVertex);
+    if (checkVersion2_0()) {
+        mShaderCache.lookup(mVertex.get(), mFragment.get());
+
+        mFragmentStore->setupGL2(this, &mStateFragmentStore);
+        mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
+        mRaster->setupGL2(this, &mStateRaster);
+        mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
+
+    } else {
+        mFragmentStore->setupGL(this, &mStateFragmentStore);
+        mFragment->setupGL(this, &mStateFragment);
+        mRaster->setupGL(this, &mStateRaster);
+        mVertex->setupGL(this, &mStateVertex);
+    }
 }
 
 static bool getProp(const char *str)
@@ -247,10 +268,6 @@
      rsc->props.mLogScripts = getProp("debug.rs.script");
      rsc->props.mLogObjects = getProp("debug.rs.objects");
 
-     //pthread_mutex_lock(&gInitMutex);
-     //rsc->initEGL();
-     //pthread_mutex_unlock(&gInitMutex);
-
      ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
      if (!tlsStruct) {
          LOGE("Error allocating tls storage");
@@ -271,6 +288,7 @@
      rsc->setFragment(NULL);
      rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
      rsc->setFragmentStore(NULL);
+     rsc->mStateVertexArray.init(rsc);
 
      rsc->mRunning = true;
      bool mDraw = true;
@@ -449,7 +467,7 @@
         if (!mEGL.mContext) {
             first = true;
             pthread_mutex_lock(&gInitMutex);
-            initEGL();
+            initEGL(false);
             pthread_mutex_unlock(&gInitMutex);
         }
 
@@ -480,14 +498,24 @@
 
             //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
             LOGV("GL Version %s", mGL.mVersion);
-            LOGV("GL Vendor %s", mGL.mVendor);
+            //LOGV("GL Vendor %s", mGL.mVendor);
             LOGV("GL Renderer %s", mGL.mRenderer);
             //LOGV("GL Extensions %s", mGL.mExtensions);
 
-            if ((strlen((const char *)mGL.mVersion) < 12) || memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
+            const char *verptr = NULL;
+            if (strlen((const char *)mGL.mVersion) > 9) {
+                if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
+                    verptr = (const char *)mGL.mVersion + 12;
+                }
+                if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) {
+                    verptr = (const char *)mGL.mVersion + 9;
+                }
+            }
+
+            if (!verptr) {
                 LOGE("Error, OpenGL ES Lite not supported");
             } else {
-                sscanf((const char *)mGL.mVersion + 13, "%i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
+                sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
             }
         }
 
diff --git a/rsContext.h b/rsContext.h
index 991e2ef..a5e73da 100644
--- a/rsContext.h
+++ b/rsContext.h
@@ -37,6 +37,8 @@
 #include "rsProgramFragmentStore.h"
 #include "rsProgramRaster.h"
 #include "rsProgramVertex.h"
+#include "rsShaderCache.h"
+#include "rsVertexArray.h"
 
 #include "rsgApiStructs.h"
 #include "rsLocklessFifo.h"
@@ -72,8 +74,10 @@
     ProgramRasterState mStateRaster;
     ProgramVertexState mStateVertex;
     LightState mStateLight;
+    VertexArrayState mStateVertexArray;
 
     ScriptCState mScriptC;
+    ShaderCache mShaderCache;
 
     void swapBuffers();
     void setRootScript(Script *);
@@ -222,7 +226,7 @@
 private:
     Context();
 
-    void initEGL();
+    void initEGL(bool useGL2);
     void deinitEGL();
 
     bool runRootScript();
diff --git a/rsProgram.cpp b/rsProgram.cpp
index ed5918b..8e9ba08 100644
--- a/rsProgram.cpp
+++ b/rsProgram.cpp
@@ -17,6 +17,9 @@
 #include "rsContext.h"
 #include "rsProgram.h"
 
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
 using namespace android;
 using namespace android::renderscript;
 
@@ -25,6 +28,10 @@
 {
     mAllocFile = __FILE__;
     mAllocLine = __LINE__;
+    mDirty = true;
+    mShaderID = 0;
+    mAttribCount = 0;
+    mUniformCount = 0;
 
     mElementIn.set(in);
     mElementOut.set(out);
@@ -51,4 +58,41 @@
     mDirty = true;
 }
 
+void Program::createShader()
+{
+}
 
+bool Program::loadShader(uint32_t type)
+{
+    mShaderID = glCreateShader(type);
+    rsAssert(mShaderID);
+
+    LOGV("Loading shader type %x", type);
+    LOGE(mShader.string());
+
+    if (mShaderID) {
+        const char * ss = mShader.string();
+        glShaderSource(mShaderID, 1, &ss, NULL);
+        glCompileShader(mShaderID);
+
+        GLint compiled = 0;
+        glGetShaderiv(mShaderID, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(mShaderID, infoLen, NULL, buf);
+                    LOGE("Could not compile shader \n%s\n", buf);
+                    free(buf);
+                }
+                glDeleteShader(mShaderID);
+                mShaderID = 0;
+                return false;
+            }
+        }
+    }
+    LOGV("--Shader load result %x ", glGetError());
+    return true;
+}
diff --git a/rsProgram.h b/rsProgram.h
index 86a46e2..06c72f9 100644
--- a/rsProgram.h
+++ b/rsProgram.h
@@ -25,14 +25,26 @@
 namespace renderscript {
 
 
+class ShaderCache;
 
 class Program : public ObjectBase
 {
 public:
+    const static uint32_t MAX_ATTRIBS = 8;
+    const static uint32_t MAX_UNIFORMS = 16;
+
     Program(Context *, Element *in, Element *out);
     virtual ~Program();
 
     void bindAllocation(Allocation *);
+    virtual void createShader();
+
+    uint32_t getShaderID() const {return mShaderID;}
+
+    uint32_t getAttribCount() const {return mAttribCount;}
+    uint32_t getUniformCount() const {return mUniformCount;}
+    const String8 & getAttribName(uint32_t i) const {return mAttribNames[i];}
+    const String8 & getUniformName(uint32_t i) const {return mUniformNames[i];}
 
 protected:
     // Components not listed in "in" will be passed though
@@ -43,7 +55,15 @@
     ObjectBaseRef<Allocation> mConstants;
 
     mutable bool mDirty;
+    String8 mShader;
+    uint32_t mShaderID;
 
+    uint32_t mAttribCount;
+    uint32_t mUniformCount;
+    String8 mAttribNames[MAX_ATTRIBS];
+    String8 mUniformNames[MAX_UNIFORMS];
+
+    bool loadShader(uint32_t type);
 
 public:
     void forceDirty() const {mDirty = true;}
diff --git a/rsProgramFragment.cpp b/rsProgramFragment.cpp
index 708a0e0..daefc2c 100644
--- a/rsProgramFragment.cpp
+++ b/rsProgramFragment.cpp
@@ -19,6 +19,8 @@
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 
 using namespace android;
 using namespace android::renderscript;
@@ -109,6 +111,97 @@
     mDirty = false;
 }
 
+void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
+{
+    //LOGE("sgl2 frag1 %x", glGetError());
+    if ((state->mLast.get() == this) && !mDirty) {
+        //return;
+    }
+    state->mLast.set(this);
+
+    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+        glActiveTexture(GL_TEXTURE0 + ct);
+        if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
+            glDisable(GL_TEXTURE_2D);
+            continue;
+        }
+
+        glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
+        if (mSamplers[ct].get()) {
+            mSamplers[ct]->setupGL();
+        } else {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        }
+
+        glEnable(GL_TEXTURE_2D);
+        glUniform1i(sc->fragUniformSlot(ct), ct);
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    mDirty = false;
+
+    //LOGE("sgl2 frag2 %x", glGetError());
+}
+
+void ProgramFragment::loadShader() {
+    Program::loadShader(GL_FRAGMENT_SHADER);
+}
+
+void ProgramFragment::createShader()
+{
+    mShader.setTo("precision mediump float;\n");
+    mShader.append("varying vec4 varColor;\n");
+    mShader.append("varying vec4 varTex0;\n");
+
+    uint32_t mask = mTextureEnableMask;
+    uint32_t texNum = 0;
+    while (mask) {
+        if (mask & 1) {
+            char buf[64];
+            mShader.append("uniform sampler2D uni_Tex");
+            sprintf(buf, "%i", texNum);
+            mShader.append(buf);
+            mShader.append(";\n");
+        }
+        mask >>= 1;
+        texNum++;
+    }
+
+
+    mShader.append("void main() {\n");
+    //mShader.append("  gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n");
+    mShader.append("  vec4 col = varColor;\n");
+
+    mask = mTextureEnableMask;
+    texNum = 0;
+    while (mask) {
+        if (mask & 1) {
+            switch(mEnvModes[texNum]) {
+            case RS_TEX_ENV_MODE_REPLACE:
+                mShader.append("  col = texture2D(uni_Tex0, varTex0.xy);\n");
+                break;
+            case RS_TEX_ENV_MODE_MODULATE:
+                mShader.append("  col *= texture2D(uni_Tex0, varTex0.xy);\n");
+                break;
+            case RS_TEX_ENV_MODE_DECAL:
+                mShader.append("  col = texture2D(uni_Tex0, varTex0.xy);\n");
+                break;
+            }
+
+        }
+        mask >>= 1;
+        texNum++;
+    }
+
+    //mShader.append("  col.a = 1.0;\n");
+    //mShader.append("  col.r = 0.5;\n");
+
+    mShader.append("  gl_FragColor = col;\n");
+    mShader.append("}\n");
+}
 
 void ProgramFragment::bindTexture(uint32_t slot, Allocation *a)
 {
@@ -173,7 +266,14 @@
     }
 }
 
+void ProgramFragment::init(Context *rsc)
+{
+    mUniformCount = 2;
+    mUniformNames[0].setTo("uni_Tex0");
+    mUniformNames[1].setTo("uni_Tex1");
 
+    createShader();
+}
 
 ProgramFragmentState::ProgramFragmentState()
 {
@@ -190,6 +290,7 @@
 {
     ProgramFragment *pf = new ProgramFragment(rsc, NULL, NULL, false);
     mDefault.set(pf);
+    pf->init(rsc);
 }
 
 void ProgramFragmentState::deinit(Context *rsc)
@@ -241,6 +342,7 @@
 {
     ProgramFragment *pf = rsc->mStateFragment.mPF;
     pf->incUserRef();
+    pf->init(rsc);
     rsc->mStateFragment.mPF = 0;
     return pf;
 }
diff --git a/rsProgramFragment.h b/rsProgramFragment.h
index e26c6e8..6fc852e 100644
--- a/rsProgramFragment.h
+++ b/rsProgramFragment.h
@@ -36,7 +36,7 @@
     virtual ~ProgramFragment();
 
     virtual void setupGL(const Context *, ProgramFragmentState *);
-
+    virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc);
 
 
     void bindTexture(uint32_t slot, Allocation *);
@@ -46,6 +46,9 @@
     void setEnvMode(uint32_t slot, RsTexEnvMode);
     void setTexEnable(uint32_t slot, bool);
 
+    virtual void createShader();
+    virtual void loadShader();
+    virtual void init(Context *rsc);
 
 
 protected:
diff --git a/rsProgramFragmentStore.cpp b/rsProgramFragmentStore.cpp
index de33d9c..d7d5c75 100644
--- a/rsProgramFragmentStore.cpp
+++ b/rsProgramFragmentStore.cpp
@@ -83,10 +83,44 @@
     } else {
         glDisable(GL_DITHER);
     }
-
-
 }
 
+void ProgramFragmentStore::setupGL2(const Context *rsc, ProgramFragmentStoreState *state)
+{
+    if (state->mLast.get() == this) {
+        return;
+    }
+    state->mLast.set(this);
+
+    glColorMask(mColorRWriteEnable,
+                mColorGWriteEnable,
+                mColorBWriteEnable,
+                mColorAWriteEnable);
+    if (mBlendEnable) {
+        glEnable(GL_BLEND);
+        glBlendFunc(mBlendSrc, mBlendDst);
+    } else {
+        glDisable(GL_BLEND);
+    }
+
+    //LOGE("pfs  %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
+
+    glDepthMask(mDepthWriteEnable);
+    if(mDepthTestEnable || mDepthWriteEnable) {
+        glEnable(GL_DEPTH_TEST);
+        glDepthFunc(mDepthFunc);
+    } else {
+        glDisable(GL_DEPTH_TEST);
+    }
+
+    if (mDitherEnable) {
+        glEnable(GL_DITHER);
+    } else {
+        glDisable(GL_DITHER);
+    }
+}
+
+
 void ProgramFragmentStore::setDitherEnable(bool enable)
 {
     mDitherEnable = enable;
diff --git a/rsProgramFragmentStore.h b/rsProgramFragmentStore.h
index a344387..3f9d8c9 100644
--- a/rsProgramFragmentStore.h
+++ b/rsProgramFragmentStore.h
@@ -32,6 +32,7 @@
     virtual ~ProgramFragmentStore();
 
     virtual void setupGL(const Context *, ProgramFragmentStoreState *);
+    virtual void setupGL2(const Context *, ProgramFragmentStoreState *);
 
     void setDepthFunc(RsDepthFunc);
     void setDepthMask(bool);
diff --git a/rsProgramRaster.cpp b/rsProgramRaster.cpp
index 51ae7cf..f0039f7 100644
--- a/rsProgramRaster.cpp
+++ b/rsProgramRaster.cpp
@@ -86,6 +86,14 @@
     }
 }
 
+void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state)
+{
+    if (state->mLast.get() == this) {
+        return;
+    }
+    state->mLast.set(this);
+}
+
 
 
 ProgramRasterState::ProgramRasterState()
diff --git a/rsProgramRaster.h b/rsProgramRaster.h
index a6d5ba8..4efecb4 100644
--- a/rsProgramRaster.h
+++ b/rsProgramRaster.h
@@ -37,6 +37,7 @@
     virtual ~ProgramRaster();
 
     virtual void setupGL(const Context *, ProgramRasterState *);
+    virtual void setupGL2(const Context *, ProgramRasterState *);
 
     void setLineWidth(float w);
     void setPointSize(float s);
diff --git a/rsProgramVertex.cpp b/rsProgramVertex.cpp
index 68f589f..1776b02 100644
--- a/rsProgramVertex.cpp
+++ b/rsProgramVertex.cpp
@@ -19,6 +19,8 @@
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 
 using namespace android;
 using namespace android::renderscript;
@@ -92,6 +94,68 @@
     mDirty = false;
 }
 
+void ProgramVertex::loadShader() {
+    Program::loadShader(GL_VERTEX_SHADER);
+}
+
+void ProgramVertex::createShader()
+{
+    mShader.setTo("");
+
+    for (uint32_t ct=0; ct < mAttribCount; ct++) {
+        mShader.append("attribute vec4 ");
+        mShader.append(mAttribNames[ct]);
+        mShader.append(";\n");
+    }
+
+    for (uint32_t ct=0; ct < mUniformCount; ct++) {
+        mShader.append("uniform mat4 ");
+        mShader.append(mUniformNames[ct]);
+        mShader.append(";\n");
+    }
+
+    mShader.append("varying vec4 varColor;\n");
+    mShader.append("varying vec4 varTex0;\n");
+
+    mShader.append("void main() {\n");
+    mShader.append("  gl_Position = uni_MVP * attrib_Position;\n");
+    mShader.append("  varColor = attrib_Color;\n");
+    if (mTextureMatrixEnable) {
+        mShader.append("  varTex0 = uni_TexMatrix * attrib_T0;\n");
+    } else {
+        mShader.append("  varTex0 = attrib_T0;\n");
+    }
+    //mShader.append("  pos.x = pos.x / 480.0;\n");
+    //mShader.append("  pos.y = pos.y / 800.0;\n");
+    //mShader.append("  gl_Position = pos;\n");
+    mShader.append("}\n");
+}
+
+void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc)
+{
+    //LOGE("sgl2 vtx1 %x", glGetError());
+    if ((state->mLast.get() == this) && !mDirty) {
+        //return;
+    }
+
+    const float *f = static_cast<const float *>(mConstants->getPtr());
+
+    Matrix mvp;
+    mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+    Matrix t;
+    t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+    mvp.multiply(&t);
+
+    glUniformMatrix4fv(sc->vtxUniformSlot(0), 1, GL_FALSE, mvp.m);
+    if (mTextureMatrixEnable) {
+        glUniformMatrix4fv(sc->vtxUniformSlot(1), 1, GL_FALSE,
+                           &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+    }
+
+    state->mLast.set(this);
+    //LOGE("sgl2 vtx2 %x", glGetError());
+}
+
 void ProgramVertex::addLight(const Light *l)
 {
     if (mLightCount < MAX_LIGHTS) {
@@ -130,6 +194,26 @@
     mvp.vectorMultiply(v4out, v3in);
 }
 
+void ProgramVertex::init(Context *rsc)
+{
+    mAttribCount = 6;
+    mAttribNames[VertexArray::POSITION].setTo("attrib_Position");
+    mAttribNames[VertexArray::COLOR].setTo("attrib_Color");
+    mAttribNames[VertexArray::NORMAL].setTo("attrib_Normal");
+    mAttribNames[VertexArray::POINT_SIZE].setTo("attrib_PointSize");
+    mAttribNames[VertexArray::TEXTURE_0].setTo("attrib_T0");
+    mAttribNames[VertexArray::TEXTURE_1].setTo("attrib_T1");
+
+    mUniformCount = 2;
+    mUniformNames[0].setTo("uni_MVP");
+    mUniformNames[1].setTo("uni_TexMatrix");
+
+    createShader();
+}
+
+
+///////////////////////////////////////////////////////////////////////
+
 ProgramVertexState::ProgramVertexState()
 {
     mPV = NULL;
@@ -154,7 +238,7 @@
     Allocation *alloc = (Allocation *)rsi_AllocationCreateTyped(rsc, mAllocType.get());
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
-
+    pv->init(rsc);
     pv->bindAllocation(alloc);
 
     updateSize(rsc, w, h);
@@ -194,6 +278,7 @@
 {
     ProgramVertex *pv = rsc->mStateVertex.mPV;
     pv->incUserRef();
+    pv->init(rsc);
     rsc->mStateVertex.mPV = 0;
     return pv;
 }
diff --git a/rsProgramVertex.h b/rsProgramVertex.h
index a97ba38..aa626da 100644
--- a/rsProgramVertex.h
+++ b/rsProgramVertex.h
@@ -34,6 +34,7 @@
     virtual ~ProgramVertex();
 
     virtual void setupGL(const Context *rsc, ProgramVertexState *state);
+    virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc);
 
 
     void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
@@ -45,6 +46,10 @@
 
     void transformToScreen(const Context *, float *v4out, const float *v3in) const;
 
+    virtual void createShader();
+    virtual void loadShader();
+    virtual void init(Context *);
+
 
 protected:
     uint32_t mLightCount;
diff --git a/rsSampler.cpp b/rsSampler.cpp
index b793750..f9bdb2e 100644
--- a/rsSampler.cpp
+++ b/rsSampler.cpp
@@ -64,7 +64,6 @@
 
     };
 
-
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
diff --git a/rsScriptC_Lib.cpp b/rsScriptC_Lib.cpp
index 23888ff..55a8391 100644
--- a/rsScriptC_Lib.cpp
+++ b/rsScriptC_Lib.cpp
@@ -26,6 +26,8 @@
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 
 #include <time.h>
 
@@ -102,8 +104,9 @@
 
 static void SC_updateSimpleMesh(RsSimpleMesh mesh)
 {
+    GET_TLS();
     SimpleMesh *sm = static_cast<SimpleMesh *>(mesh);
-    sm->uploadAll();
+    sm->uploadAll(rsc);
 }
 
 static uint32_t SC_loadU32(uint32_t bank, uint32_t offset)
@@ -683,13 +686,13 @@
     rsc->setupCheck();
 
     float vtx[] = { x1, y1, z1, x2, y2, z2 };
-
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glVertexPointer(3, GL_FLOAT, 0, vtx);
-
-    glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_COLOR_ARRAY);
+    VertexArray va;
+    va.setPosition(2, GL_FLOAT, 12, (uint32_t)&vtx);
+    if (rsc->checkVersion2_0()) {
+        va.setupGL2(&rsc->mStateVertexArray, &rsc->mShaderCache);
+    } else {
+        va.setupGL(&rsc->mStateVertexArray);
+    }
 
     glDrawArrays(GL_LINES, 0, 2);
 }
@@ -701,12 +704,13 @@
 
     float vtx[] = { x, y, z };
 
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glVertexPointer(3, GL_FLOAT, 0, vtx);
-
-    glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_COLOR_ARRAY);
+    VertexArray va;
+    va.setPosition(1, GL_FLOAT, 12, (uint32_t)&vtx);
+    if (rsc->checkVersion2_0()) {
+        va.setupGL2(&rsc->mStateVertexArray, &rsc->mShaderCache);
+    } else {
+        va.setupGL(&rsc->mStateVertexArray);
+    }
 
     glDrawArrays(GL_POINTS, 0, 1);
 }
@@ -721,6 +725,7 @@
                                  float u4, float v4)
 {
     GET_TLS();
+    rsc->setupCheck();
 
     //LOGE("Quad");
     //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
@@ -731,26 +736,17 @@
     float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
     const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
 
-    rsc->setupCheck();
+    VertexArray va;
+    va.setPosition(2, GL_FLOAT, 12, (uint32_t)&vtx);
+    va.setTexture(2, GL_FLOAT, 8, (uint32_t)&tex, 0);
+    //va.setTexture(2, GL_FLOAT, 8, (uint32_t)&tex, 1);
+    //
+    if (rsc->checkVersion2_0()) {
+        va.setupGL2(&rsc->mStateVertexArray, &rsc->mShaderCache);
+    } else {
+        va.setupGL(&rsc->mStateVertexArray);
+    }
 
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
-
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glVertexPointer(3, GL_FLOAT, 0, vtx);
-
-    glClientActiveTexture(GL_TEXTURE0);
-    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-    glTexCoordPointer(2, GL_FLOAT, 0, tex);
-    glClientActiveTexture(GL_TEXTURE1);
-    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-    glTexCoordPointer(2, GL_FLOAT, 0, tex);
-    glClientActiveTexture(GL_TEXTURE0);
-
-    glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_COLOR_ARRAY);
-
-    //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
 
     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 }
@@ -769,18 +765,24 @@
 static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h)
 {
     GET_TLS();
-    rsc->setupCheck();
+    ObjectBaseRef<const ProgramVertex> tmp(rsc->getVertex());
+    rsc->setVertex(rsc->getDefaultProgramVertex());
+    //rsc->setupCheck();
 
-    GLint crop[4] = {0, h, w, -h};
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
-    glDrawTexfOES(x, y, z, w, h);
+    //GLint crop[4] = {0, h, w, -h};
+
+    float sh = rsc->getHeight();
+
+    SC_drawQuad(x,   sh - y,     z,
+                x+w, sh - y,     z,
+                x+w, sh - (y+h), z,
+                x,   sh - (y+h), z);
+    rsc->setVertex((ProgramVertex *)tmp.get());
 }
 
 static void SC_drawSprite(float x, float y, float z, float w, float h)
 {
     GET_TLS();
-    rsc->setupCheck();
-
     float vin[3] = {x, y, z};
     float vout[4];
 
@@ -802,9 +804,8 @@
     //LOGE("ds  out2 %f %f %f", vout[0], vout[1], vout[2]);
 
     // U, V, W, H
-    GLint crop[4] = {0, h, w, -h};
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
-    glDrawTexiOES(vout[0], vout[1], 0/*vout[2]*/, w, h);
+    SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w);
+    //rsc->setupCheck();
 }
 
 
@@ -822,7 +823,7 @@
     GET_TLS();
     SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
     rsc->setupCheck();
-    sm->render();
+    sm->render(rsc);
 }
 
 static void SC_drawSimpleMeshRange(RsSimpleMesh vsm, uint32_t start, uint32_t len)
@@ -830,7 +831,7 @@
     GET_TLS();
     SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
     rsc->setupCheck();
-    sm->renderRange(start, len);
+    sm->renderRange(rsc, start, len);
 }
 
 
@@ -840,7 +841,12 @@
 
 static void SC_color(float r, float g, float b, float a)
 {
-    glColor4f(r, g, b, a);
+    GET_TLS();
+    if (rsc->checkVersion2_0()) {
+        glVertexAttrib4f(1, r, g, b, a);
+    } else {
+        glColor4f(r, g, b, a);
+    }
 }
 
 static void SC_ambient(float r, float g, float b, float a)
@@ -945,9 +951,14 @@
 
 static void SC_hsb(float h, float s, float b, float a)
 {
+    GET_TLS();
     float rgb[3];
     SC_hsbToRgb(h, s, b, rgb);
-    glColor4f(rgb[0], rgb[1], rgb[2], a);
+    if (rsc->checkVersion2_0()) {
+        glVertexAttrib4f(1, rgb[0], rgb[1], rgb[2], a);
+    } else {
+        glColor4f(rgb[0], rgb[1], rgb[2], a);
+    }
 }
 
 static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
diff --git a/rsShaderCache.cpp b/rsShaderCache.cpp
new file mode 100644
index 0000000..2727e6c
--- /dev/null
+++ b/rsShaderCache.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009 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 "rsContext.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ShaderCache::ShaderCache()
+{
+    mEntryCount = 0;
+    mEntryAllocationCount = 16;
+    mEntries = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
+}
+
+ShaderCache::~ShaderCache()
+{
+    for (uint32_t ct=0; ct < mEntryCount; ct++) {
+        glDeleteProgram(mEntries[ct].program);
+    }
+
+    mEntryCount = 0;
+    mEntryAllocationCount = 0;
+    free(mEntries);
+}
+
+bool ShaderCache::lookup(ProgramVertex *vtx, ProgramFragment *frag)
+{
+    if (!vtx->getShaderID()) {
+        vtx->loadShader();
+    }
+    if (!frag->getShaderID()) {
+        frag->loadShader();
+    }
+
+    for (uint32_t ct=0; ct < mEntryCount; ct++) {
+        if ((mEntries[ct].vtx == vtx->getShaderID()) &&
+            (mEntries[ct].frag == frag->getShaderID())) {
+
+            //LOGV("SC using program %i", mEntries[ct].program);
+            glUseProgram(mEntries[ct].program);
+            mCurrent = &mEntries[ct];
+            return true;
+        }
+    }
+    // Not in cache, add it.
+
+    if (mEntryAllocationCount == mEntryCount) {
+        // Out of space, make some.
+        mEntryAllocationCount *= 2;
+        entry_t *e = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
+        if (!e) {
+            LOGE("Out of memory for ShaderCache::lookup");
+            return false;
+        }
+        memcpy(e, mEntries, sizeof(entry_t) * mEntryCount);
+        free(mEntries);
+        mEntries = e;
+    }
+
+    LOGV("vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID());
+    LOGE("e0 %x", glGetError());
+
+    entry_t *e = &mEntries[mEntryCount];
+    mCurrent = e;
+    e->vtx = vtx->getShaderID();
+    e->frag = frag->getShaderID();
+    e->program = glCreateProgram();
+    if (mEntries[mEntryCount].program) {
+        GLuint pgm = e->program;
+        glAttachShader(pgm, vtx->getShaderID());
+        //LOGE("e1 %x", glGetError());
+        glAttachShader(pgm, frag->getShaderID());
+
+        glBindAttribLocation(pgm, VertexArray::POSITION, "attrib_Position");
+        glBindAttribLocation(pgm, VertexArray::COLOR, "attrib_Color");
+
+
+        //LOGE("e2 %x", glGetError());
+        glLinkProgram(pgm);
+        //LOGE("e3 %x", glGetError());
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(pgm, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(pgm, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(pgm, bufLength, NULL, buf);
+                    LOGE("Could not link program:\n%s\n", buf);
+                    free(buf);
+                }
+            }
+            glDeleteProgram(pgm);
+        }
+        for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) {
+            e->mVtxAttribSlots[ct] = glGetAttribLocation(pgm, vtx->getAttribName(ct));
+            LOGV("vtx A, %s = %d\n", vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]);
+        }
+        for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
+            e->mVtxUniformSlots[ct] = glGetUniformLocation(pgm, vtx->getUniformName(ct));
+            LOGV("vtx U, %s = %d\n", vtx->getUniformName(ct).string(), e->mVtxUniformSlots[ct]);
+        }
+        for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
+            e->mFragUniformSlots[ct] = glGetUniformLocation(pgm, frag->getUniformName(ct));
+            LOGV("frag U, %s = %d\n", frag->getUniformName(ct).string(), e->mFragUniformSlots[ct]);
+        }
+    }
+
+    LOGV("SC made program %i", e->program);
+    glUseProgram(e->program);
+    mEntryCount++;
+    return true;
+}
+
+void ShaderCache::cleanupVertex(uint32_t id)
+{
+}
+
+void ShaderCache::cleanupFragment(uint32_t id)
+{
+}
+
+void ShaderCache::cleanupAll()
+{
+}
+
diff --git a/rsShaderCache.h b/rsShaderCache.h
new file mode 100644
index 0000000..4d9f8ec
--- /dev/null
+++ b/rsShaderCache.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 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_SHADER_CACHE_H
+#define ANDROID_SHADER_CACHE_H
+
+
+#include "rsObjectBase.h"
+#include "rsVertexArray.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class ShaderCache
+{
+public:
+    ShaderCache();
+    virtual ~ShaderCache();
+
+    bool lookup(ProgramVertex *, ProgramFragment *);
+
+    void cleanupVertex(uint32_t id);
+    void cleanupFragment(uint32_t id);
+
+    void cleanupAll();
+
+    int32_t vtxAttribSlot(uint32_t a) const {return mCurrent->mVtxAttribSlots[a];}
+    int32_t vtxUniformSlot(uint32_t a) const {return mCurrent->mVtxUniformSlots[a];}
+    int32_t fragAttribSlot(uint32_t a) const {return mCurrent->mFragAttribSlots[a];}
+    int32_t fragUniformSlot(uint32_t a) const {return mCurrent->mFragUniformSlots[a];}
+
+protected:
+    typedef struct {
+        uint32_t vtx;
+        uint32_t frag;
+        uint32_t program;
+        int32_t mVtxAttribSlots[Program::MAX_ATTRIBS];
+        int32_t mVtxUniformSlots[Program::MAX_UNIFORMS];
+        int32_t mFragAttribSlots[Program::MAX_ATTRIBS];
+        int32_t mFragUniformSlots[Program::MAX_UNIFORMS];
+    } entry_t;
+    entry_t *mEntries;
+    entry_t *mCurrent;
+
+    uint32_t mEntryCount;
+    uint32_t mEntryAllocationCount;
+
+};
+
+
+
+}
+}
+#endif //ANDROID_SHADER_CACHE_H
+
+
+
+
diff --git a/rsSimpleMesh.cpp b/rsSimpleMesh.cpp
index b082fd7..edfe967 100644
--- a/rsSimpleMesh.cpp
+++ b/rsSimpleMesh.cpp
@@ -34,39 +34,36 @@
     delete[] mVertexBuffers;
 }
 
-void SimpleMesh::render() const
+void SimpleMesh::render(Context *rsc) const
 {
     if (mPrimitiveType.get()) {
-        renderRange(0, mPrimitiveType->getDimX());
+        renderRange(rsc, 0, mPrimitiveType->getDimX());
         return;
     }
 
     if (mIndexType.get()) {
-        renderRange(0, mIndexType->getDimX());
+        renderRange(rsc, 0, mIndexType->getDimX());
         return;
     }
 
-    renderRange(0, mVertexTypes[0]->getDimX());
+    renderRange(rsc, 0, mVertexTypes[0]->getDimX());
 }
 
-void SimpleMesh::renderRange(uint32_t start, uint32_t len) const
+void SimpleMesh::renderRange(Context *rsc, uint32_t start, uint32_t len) const
 {
     if (len < 1) {
         return;
     }
 
-    glDisableClientState(GL_VERTEX_ARRAY);
-    glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_COLOR_ARRAY);
-    for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
-        glClientActiveTexture(GL_TEXTURE0 + ct);
-        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-    }
-    glClientActiveTexture(GL_TEXTURE0);
-
+    VertexArray va;
     for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
-        glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[ct]->getBufferObjectID());
-        mVertexTypes[ct]->enableGLVertexBuffer();
+        va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
+        mVertexTypes[ct]->enableGLVertexBuffer(&va);
+    }
+    if (rsc->checkVersion2_0()) {
+        va.setupGL2(0, &rsc->mShaderCache);
+    } else {
+        va.setupGL(0);
     }
 
     if (mIndexType.get()) {
@@ -77,7 +74,7 @@
     }
 }
 
-void SimpleMesh::uploadAll()
+void SimpleMesh::uploadAll(Context *rsc)
 {
     for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
         if (mVertexBuffers[ct].get()) {
diff --git a/rsSimpleMesh.h b/rsSimpleMesh.h
index 1e5c908..6defbda 100644
--- a/rsSimpleMesh.h
+++ b/rsSimpleMesh.h
@@ -45,9 +45,9 @@
     uint32_t mGLPrimitive;
 
 
-    void render() const;
-    void renderRange(uint32_t start, uint32_t len) const;
-    void uploadAll();
+    void render(Context *) const;
+    void renderRange(Context *, uint32_t start, uint32_t len) const;
+    void uploadAll(Context *);
 
 
 protected:
diff --git a/rsType.cpp b/rsType.cpp
index ddaa2f0..044ec46 100644
--- a/rsType.cpp
+++ b/rsType.cpp
@@ -245,7 +245,7 @@
     }
 }
 
-void Type::enableGLVertexBuffer() const
+void Type::enableGLVertexBuffer(VertexArray *va) const
 {
     // Note: We are only going to enable buffers and never disable them
     // here.  The reasonis more than one Allocation may be used as a vertex
@@ -254,49 +254,39 @@
 
     uint32_t stride = mElement->getSizeBytes();
     if (mGL.mVtx.size) {
-        //LOGE("va vtx %i %x, %i, %p", mGL.mVtx.size, mGL.mVtx.type, stride, (void *)mGL.mVtx.offset);
-        glEnableClientState(GL_VERTEX_ARRAY);
-        glVertexPointer(mGL.mVtx.size,
+        va->setPosition(mGL.mVtx.size,
                         mGL.mVtx.type,
                         stride,
-                        (void *)mGL.mVtx.offset);
+                        mGL.mVtx.offset);
     }
 
     if (mGL.mNorm.size) {
-        //LOGE("va norm %i %x, %i, %p", mGL.mNorm.size, mGL.mNorm.type, stride, (void *)mGL.mNorm.offset);
-        glEnableClientState(GL_NORMAL_ARRAY);
-        rsAssert(mGL.mNorm.size == 3);
-        glNormalPointer(mGL.mNorm.type,
-                        stride,
-                        (void *)mGL.mNorm.offset);
+        va->setNormal(mGL.mNorm.type,
+                      stride,
+                      mGL.mNorm.offset);
     }
 
     if (mGL.mColor.size) {
-        glEnableClientState(GL_COLOR_ARRAY);
-        glColorPointer(mGL.mColor.size,
-                       mGL.mColor.type,
-                       stride,
-                       (void *)mGL.mColor.offset);
+        va->setColor(mGL.mColor.size,
+                     mGL.mColor.type,
+                     stride,
+                     mGL.mColor.offset);
     }
 
     for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
         if (mGL.mTex[ct].size) {
-            //LOGE("va tex%i %i %x, %i, %p", ct, mGL.mTex[ct].size, mGL.mTex[ct].type, stride, (void *)mGL.mTex[ct].offset);
-            glClientActiveTexture(GL_TEXTURE0 + ct);
-            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-            glTexCoordPointer(mGL.mTex[ct].size,
-                              mGL.mTex[ct].type,
-                              stride,
-                              (void *)mGL.mTex[ct].offset);
+            va->setTexture(mGL.mTex[ct].size,
+                           mGL.mTex[ct].type,
+                           stride,
+                           mGL.mTex[ct].offset,
+                           ct);
         }
     }
-    glClientActiveTexture(GL_TEXTURE0);
 
     if (mGL.mPointSize.size) {
-        glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
-        glPointSizePointerOES(mGL.mPointSize.type,
-                              stride,
-                              (void *)mGL.mPointSize.offset);
+        va->setPointSize(mGL.mPointSize.type,
+                         stride,
+                         mGL.mPointSize.offset);
     }
 
 }
diff --git a/rsType.h b/rsType.h
index 2c43405..d261d58 100644
--- a/rsType.h
+++ b/rsType.h
@@ -67,7 +67,7 @@
     void clear();
     void compute();
 
-    void enableGLVertexBuffer() const;
+    void enableGLVertexBuffer(class VertexArray *) const;
 
     void dumpLOGV(const char *prefix) const;
 
diff --git a/rsVertexArray.cpp b/rsVertexArray.cpp
new file mode 100644
index 0000000..34d42ed
--- /dev/null
+++ b/rsVertexArray.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2009 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 "rsContext.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+VertexArray::VertexArray()
+{
+    memset(mAttribs, 0, sizeof(mAttribs));
+    mActiveBuffer = 0;
+}
+
+VertexArray::~VertexArray()
+{
+}
+
+
+void VertexArray::clearAll()
+{
+    memset(mAttribs, 0, sizeof(mAttribs));
+    mActiveBuffer = 0;
+}
+
+void VertexArray::clear(AttribName n)
+{
+    mAttribs[n].size = 0;
+}
+
+void VertexArray::setPosition(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset)
+{
+    mAttribs[POSITION].buffer = mActiveBuffer;
+    mAttribs[POSITION].type = type;
+    mAttribs[POSITION].size = size;
+    mAttribs[POSITION].offset = offset;
+    mAttribs[POSITION].stride = stride;
+    mAttribs[POSITION].normalized = false;
+}
+
+void VertexArray::setColor(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset)
+{
+    mAttribs[COLOR].buffer = mActiveBuffer;
+    mAttribs[COLOR].type = type;
+    mAttribs[COLOR].size = size;
+    mAttribs[COLOR].offset = offset;
+    mAttribs[COLOR].stride = stride;
+    mAttribs[COLOR].normalized = type != GL_FLOAT;
+}
+
+void VertexArray::setNormal(uint32_t type, uint32_t stride, uint32_t offset)
+{
+    mAttribs[NORMAL].buffer = mActiveBuffer;
+    mAttribs[NORMAL].type = type;
+    mAttribs[NORMAL].size = 3;
+    mAttribs[NORMAL].offset = offset;
+    mAttribs[NORMAL].stride = stride;
+    mAttribs[NORMAL].normalized = type != GL_FLOAT;
+}
+
+void VertexArray::setPointSize(uint32_t type, uint32_t stride, uint32_t offset)
+{
+    mAttribs[POINT_SIZE].buffer = mActiveBuffer;
+    mAttribs[POINT_SIZE].type = type;
+    mAttribs[POINT_SIZE].size = 1;
+    mAttribs[POINT_SIZE].offset = offset;
+    mAttribs[POINT_SIZE].stride = stride;
+    mAttribs[POINT_SIZE].normalized = false;
+}
+
+void VertexArray::setTexture(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset, uint32_t num)
+{
+    mAttribs[TEXTURE_0 + num].buffer = mActiveBuffer;
+    mAttribs[TEXTURE_0 + num].type = type;
+    mAttribs[TEXTURE_0 + num].size = size;
+    mAttribs[TEXTURE_0 + num].offset = offset;
+    mAttribs[TEXTURE_0 + num].stride = stride;
+    mAttribs[TEXTURE_0 + num].normalized = false;
+}
+
+void VertexArray::logAttrib(uint32_t idx) const {
+    LOGE("va %i: buf=%i  size=%i  type=0x%x  stride=0x%x  offset=0x%x", idx,
+         mAttribs[idx].buffer,
+         mAttribs[idx].size,
+         mAttribs[idx].type,
+         mAttribs[idx].stride,
+         mAttribs[idx].offset);
+}
+
+void VertexArray::setupGL(class VertexArrayState *state) const
+{
+    if (mAttribs[POSITION].size) {
+        //logAttrib(POSITION);
+        glEnableClientState(GL_VERTEX_ARRAY);
+        glBindBuffer(GL_ARRAY_BUFFER, mAttribs[POSITION].buffer);
+        glVertexPointer(mAttribs[POSITION].size,
+                        mAttribs[POSITION].type,
+                        mAttribs[POSITION].stride,
+                        (void *)mAttribs[POSITION].offset);
+    } else {
+        rsAssert(0);
+    }
+
+    if (mAttribs[NORMAL].size) {
+        //logAttrib(NORMAL);
+        glEnableClientState(GL_NORMAL_ARRAY);
+        rsAssert(mAttribs[NORMAL].size == 3);
+        glBindBuffer(GL_ARRAY_BUFFER, mAttribs[NORMAL].buffer);
+        glNormalPointer(mAttribs[NORMAL].type,
+                        mAttribs[NORMAL].stride,
+                        (void *)mAttribs[NORMAL].offset);
+    } else {
+        glDisableClientState(GL_NORMAL_ARRAY);
+    }
+
+    if (mAttribs[COLOR].size) {
+        //logAttrib(COLOR);
+        glEnableClientState(GL_COLOR_ARRAY);
+        glBindBuffer(GL_ARRAY_BUFFER, mAttribs[COLOR].buffer);
+        glColorPointer(mAttribs[COLOR].size,
+                       mAttribs[COLOR].type,
+                       mAttribs[COLOR].stride,
+                       (void *)mAttribs[COLOR].offset);
+    } else {
+        glDisableClientState(GL_COLOR_ARRAY);
+    }
+
+    for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
+        glClientActiveTexture(GL_TEXTURE0 + ct);
+        if (mAttribs[TEXTURE_0 + ct].size) {
+            //logAttrib(TEXTURE_0 + ct);
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[TEXTURE_0 + ct].buffer);
+            glTexCoordPointer(mAttribs[TEXTURE_0 + ct].size,
+                              mAttribs[TEXTURE_0 + ct].type,
+                              mAttribs[TEXTURE_0 + ct].stride,
+                              (void *)mAttribs[TEXTURE_0 + ct].offset);
+        } else {
+            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        }
+    }
+    glClientActiveTexture(GL_TEXTURE0);
+
+    if (mAttribs[POINT_SIZE].size) {
+        //logAttrib(POINT_SIZE);
+        glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+        glBindBuffer(GL_ARRAY_BUFFER, mAttribs[POINT_SIZE].buffer);
+        glPointSizePointerOES(mAttribs[POINT_SIZE].type,
+                              mAttribs[POINT_SIZE].stride,
+                              (void *)mAttribs[POINT_SIZE].offset);
+    } else {
+        glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
+    }
+}
+
+void VertexArray::setupGL2(class VertexArrayState *state, ShaderCache *sc) const
+{
+    for (int ct=1; ct < _LAST; ct++) {
+        glDisableVertexAttribArray(ct);
+    }
+
+    for (int ct=0; ct < _LAST; ct++) {
+        if (mAttribs[ct].size) {
+            //logAttrib(ct);
+            glEnableVertexAttribArray(sc->vtxAttribSlot(ct));
+            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
+            //LOGV("attp %i %i", ct, sc->vtxAttribSlot(ct));
+
+            glVertexAttribPointer(sc->vtxAttribSlot(ct),
+                                  mAttribs[ct].size,
+                                  mAttribs[ct].type,
+                                  mAttribs[ct].normalized,
+                                  mAttribs[ct].stride,
+                                  (void *)mAttribs[ct].offset);
+        } else {
+            //glDisableVertexAttribArray(ct);
+            rsAssert(ct);
+        }
+    }
+}
+////////////////////////////////////////////
+
+void VertexArrayState::init(Context *) {
+    memset(this, 0, sizeof(this));
+}
+
diff --git a/rsVertexArray.h b/rsVertexArray.h
new file mode 100644
index 0000000..235ffef
--- /dev/null
+++ b/rsVertexArray.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 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_VERTEX_ARRAY_H
+#define ANDROID_VERTEX_ARRAY_H
+
+
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ShaderCache;
+
+// An element is a group of Components that occupies one cell in a structure.
+class VertexArray
+{
+public:
+    VertexArray();
+    virtual ~VertexArray();
+
+    enum AttribName {
+        POSITION,
+        COLOR,
+        NORMAL,
+        POINT_SIZE,
+        TEXTURE_0,
+        TEXTURE_1,
+        _LAST
+    };
+
+    typedef struct {
+        uint32_t buffer;
+        uint32_t offset;
+        uint32_t type;
+        uint32_t size;
+        uint32_t stride;
+        bool normalized;
+    } Attrib;
+
+
+    void clearAll();
+    void clear(AttribName);
+
+    void setActiveBuffer(uint32_t id) {mActiveBuffer = id;}
+
+    void setPosition(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset);
+    void setColor(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset);
+    void setNormal(uint32_t type, uint32_t stride, uint32_t offset);
+    void setPointSize(uint32_t type, uint32_t stride, uint32_t offset);
+    void setTexture(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset, uint32_t num);
+
+    void setupGL(class VertexArrayState *) const;
+    void setupGL2(class VertexArrayState *, ShaderCache *) const;
+    void logAttrib(uint32_t idx) const;
+
+protected:
+    uint32_t mActiveBuffer;
+    Attrib mAttribs[_LAST];
+};
+
+
+class VertexArrayState {
+public:
+    void init(Context *);
+
+    VertexArray::Attrib mAttribs[VertexArray::_LAST];
+};
+
+
+}
+}
+#endif //ANDROID_LIGHT_H
+
+
+