More complete support for named attribs.  Adds user typed attribs as available to programVertex.  Non user attribs are not treated like user for GL2 for simplicity.
diff --git a/RenderScript.h b/RenderScript.h
index bb2c06a..6662333 100644
--- a/RenderScript.h
+++ b/RenderScript.h
@@ -64,6 +64,7 @@
 void rsContextDeinitToClient(RsContext);
 
 #define RS_MAX_TEXTURE 2
+#define RS_MAX_ATTRIBS 16
 
 enum RsDataType {
     RS_TYPE_NONE,
diff --git a/rsContext.cpp b/rsContext.cpp
index 181902b..261b827 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -274,8 +274,8 @@
 
      rsc->props.mLogTimes = getProp("debug.rs.profile");
      rsc->props.mLogScripts = getProp("debug.rs.script");
-     rsc->props.mLogObjects = getProp("debug.rs.objects");
-     rsc->props.mLogShaders = getProp("debug.rs.shaders");
+     rsc->props.mLogObjects = getProp("debug.rs.object");
+     rsc->props.mLogShaders = getProp("debug.rs.shader");
 
      ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
      if (!tlsStruct) {
diff --git a/rsProgram.h b/rsProgram.h
index dc3a57c..4bb7802 100644
--- a/rsProgram.h
+++ b/rsProgram.h
@@ -42,6 +42,8 @@
     void bindAllocation(Allocation *);
     virtual void createShader();
 
+    bool isUserProgram() const {return mUserShader.size() > 0;}
+
     void bindTexture(uint32_t slot, Allocation *);
     void bindSampler(uint32_t slot, Sampler *);
 
diff --git a/rsProgramFragment.cpp b/rsProgramFragment.cpp
index f7394a6..00f19ae 100644
--- a/rsProgramFragment.cpp
+++ b/rsProgramFragment.cpp
@@ -136,6 +136,7 @@
     }
     glActiveTexture(GL_TEXTURE0);
     mDirty = false;
+    rsc->checkError("ProgramFragment::setupGL");
 }
 
 void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
@@ -170,8 +171,7 @@
 
     glActiveTexture(GL_TEXTURE0);
     mDirty = false;
-
-    //LOGE("sgl2 frag2 %x", glGetError());
+    rsc->checkError("ProgramFragment::setupGL2");
 }
 
 void ProgramFragment::loadShader(Context *rsc) {
diff --git a/rsProgramVertex.cpp b/rsProgramVertex.cpp
index b207558..2be6a7d 100644
--- a/rsProgramVertex.cpp
+++ b/rsProgramVertex.cpp
@@ -148,21 +148,21 @@
         }
         mShader.append(mUserShader);
     } else {
-        for (uint32_t ct=0; ct < mAttribCount; ct++) {
+        for (uint32_t ct=VertexArray::POSITION; ct < mAttribCount; ct++) {
             mShader.append("attribute vec4 ");
             mShader.append(mAttribNames[ct]);
             mShader.append(";\n");
         }
 
         mShader.append("void main() {\n");
-        mShader.append("  gl_Position = uni_MVP * attrib_Position;\n");
-        mShader.append("  gl_PointSize = attrib_PointSize.x;\n");
+        mShader.append("  gl_Position = uni_MVP * ATTRIB_Position;\n");
+        mShader.append("  gl_PointSize = ATTRIB_PointSize.x;\n");
 
-        mShader.append("  varColor = attrib_Color;\n");
+        mShader.append("  varColor = ATTRIB_Color;\n");
         if (mTextureMatrixEnable) {
-            mShader.append("  varTex0 = uni_TexMatrix * attrib_T0;\n");
+            mShader.append("  varTex0 = uni_TexMatrix * ATTRIB_Texture;\n");
         } else {
-            mShader.append("  varTex0 = attrib_T0;\n");
+            mShader.append("  varTex0 = ATTRIB_Texture;\n");
         }
         //mShader.append("  pos.x = pos.x / 480.0;\n");
         //mShader.append("  pos.y = pos.y / 800.0;\n");
@@ -195,7 +195,7 @@
     }
 
     state->mLast.set(this);
-    //LOGE("sgl2 vtx2 %x", glGetError());
+    rsc->checkError("ProgramVertex::setupGL2");
 }
 
 void ProgramVertex::addLight(const Light *l)
@@ -236,15 +236,37 @@
     mvp.vectorMultiply(v4out, v3in);
 }
 
+void ProgramVertex::initAddUserAttrib(const Element *e)
+{
+    rsAssert(e->getFieldCount());
+    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
+        const Element *ce = e->getField(ct);
+        if (ce->getFieldCount()) {
+            initAddUserAttrib(ce);
+        } else {
+            String8 tmp("ATTRIB_");
+            tmp.append(e->getFieldName(ct));
+            mAttribNames[mAttribCount].setTo(tmp.string());
+            mAttribCount++;
+        }
+    }
+}
+
 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");
+    if (mUserShader.size() > 0) {
+        mAttribCount = 0;
+        for (uint32_t ct=0; ct < mInputCount; ct++) {
+            initAddUserAttrib(mInputElements[ct].get());
+        }
+    } else {
+        mAttribCount = 5;
+        mAttribNames[0].setTo("ATTRIB_Position");
+        mAttribNames[1].setTo("ATTRIB_Color");
+        mAttribNames[2].setTo("ATTRIB_Normal");
+        mAttribNames[3].setTo("ATTRIB_PointSize");
+        mAttribNames[4].setTo("ATTRIB_Texture");
+    }
 
     mUniformCount = 2;
     mUniformNames[0].setTo("uni_MVP");
diff --git a/rsProgramVertex.h b/rsProgramVertex.h
index 8c63d82..dcb988c 100644
--- a/rsProgramVertex.h
+++ b/rsProgramVertex.h
@@ -59,6 +59,9 @@
 
     // Hacks to create a program for now
     bool mTextureMatrixEnable;
+
+private:
+    void initAddUserAttrib(const Element *e);
 };
 
 
diff --git a/rsScriptC_Lib.cpp b/rsScriptC_Lib.cpp
index 917f294..8a0ab71 100644
--- a/rsScriptC_Lib.cpp
+++ b/rsScriptC_Lib.cpp
@@ -738,9 +738,7 @@
 
     VertexArray va;
     va.setPosition(3, 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);
-    //
+    va.setTexture(2, GL_FLOAT, 8, (uint32_t)&tex);
     if (rsc->checkVersion2_0()) {
         va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
     } else {
diff --git a/rsShaderCache.cpp b/rsShaderCache.cpp
index 311e3f5..0d9863d 100644
--- a/rsShaderCache.cpp
+++ b/rsShaderCache.cpp
@@ -59,6 +59,7 @@
             glUseProgram(mEntries[ct].program);
             mCurrent = &mEntries[ct];
             //LOGV("ShaderCache hit, using %i", ct);
+            rsc->checkError("ShaderCache::lookup (hit)");
             return true;
         }
     }
@@ -91,12 +92,15 @@
         //LOGE("e1 %x", glGetError());
         glAttachShader(pgm, frag->getShaderID());
 
-        glBindAttribLocation(pgm, VertexArray::POSITION, "attrib_Position");
-        glBindAttribLocation(pgm, VertexArray::COLOR, "attrib_Color");
-        //glBindAttribLocation(pgm, VertexArray::NORMAL, "attrib_Normal");
-        //glBindAttribLocation(pgm, VertexArray::POINT_SIZE, "attrib_PointSize");
-        //glBindAttribLocation(pgm, VertexArray::TEXTURE_0, "attrib_T0");
-        //glBindAttribLocation(pgm, VertexArray::TEXTURE_1, "attrib_T1");
+        if (!vtx->isUserProgram()) {
+            glBindAttribLocation(pgm, VertexArray::POSITION, "ATTRIB_Position");
+            glBindAttribLocation(pgm, VertexArray::COLOR, "ATTRIB_Color");
+            glBindAttribLocation(pgm, VertexArray::NORMAL, "ATTRIB_Normal");
+            glBindAttribLocation(pgm, VertexArray::POINT_SIZE, "ATTRIB_PointSize");
+            glBindAttribLocation(pgm, VertexArray::TEXTURE, "ATTRIB_T0");
+        } else {
+
+        }
 
         //LOGE("e2 %x", glGetError());
         glLinkProgram(pgm);
@@ -119,7 +123,7 @@
         for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) {
             e->mVtxAttribSlots[ct] = glGetAttribLocation(pgm, vtx->getAttribName(ct));
             if (rsc->props.mLogShaders) {
-                LOGV("vtx A, %s = %d\n", vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]);
+                LOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]);
             }
         }
         for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
@@ -139,6 +143,7 @@
     //LOGV("SC made program %i", e->program);
     glUseProgram(e->program);
     mEntryCount++;
+    rsc->checkError("ShaderCache::lookup (miss)");
     return true;
 }
 
diff --git a/rsSimpleMesh.cpp b/rsSimpleMesh.cpp
index 5f5622d..a819c07 100644
--- a/rsSimpleMesh.cpp
+++ b/rsSimpleMesh.cpp
@@ -55,18 +55,25 @@
         return;
     }
 
+    rsc->checkError("SimpleMesh::renderRange 1");
     VertexArray va;
-    for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
-        mVertexBuffers[ct]->uploadCheck(rsc);
-        va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
-        mVertexTypes[ct]->enableGLVertexBuffer(&va);
-    }
     if (rsc->checkVersion2_0()) {
+        for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
+            mVertexBuffers[ct]->uploadCheck(rsc);
+            va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
+            mVertexTypes[ct]->enableGLVertexBuffer2(&va);
+        }
         va.setupGL2(rsc, 0, &rsc->mShaderCache);
     } else {
+        for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
+            mVertexBuffers[ct]->uploadCheck(rsc);
+            va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
+            mVertexTypes[ct]->enableGLVertexBuffer(&va);
+        }
         va.setupGL(rsc, 0);
     }
 
+    rsc->checkError("SimpleMesh::renderRange 2");
     if (mIndexType.get()) {
         mIndexBuffer->uploadCheck(rsc);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
diff --git a/rsType.cpp b/rsType.cpp
index e7bcb08..d3b53d6 100644
--- a/rsType.cpp
+++ b/rsType.cpp
@@ -26,7 +26,6 @@
     mAllocLine = __LINE__;
     mLODs = 0;
     mLODCount = 0;
-    memset(&mGL, 0, sizeof(mGL));
     clear();
 }
 
@@ -133,18 +132,28 @@
 
 void Type::makeGLComponents()
 {
-    uint32_t texNum = 0;
-    memset(&mGL, 0, sizeof(mGL));
+    uint32_t userNum = 0;
 
     for (uint32_t ct=0; ct < getElement()->getFieldCount(); ct++) {
         const Component &c = getElement()->getField(ct)->getComponent();
 
         switch(c.getKind()) {
+        case RS_KIND_USER:
+            mGL.mUser[userNum].size = c.getVectorSize();
+            mGL.mUser[userNum].offset = mElement->getFieldOffsetBytes(ct);
+            mGL.mUser[userNum].type = c.getGLType();
+            mGL.mUser[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
+            mGL.mUser[userNum].name.setTo(getElement()->getFieldName(ct));
+            userNum ++;
+            break;
+
         case RS_KIND_POSITION:
             rsAssert(mGL.mVtx.size == 0);
             mGL.mVtx.size = c.getVectorSize();
             mGL.mVtx.offset = mElement->getFieldOffsetBytes(ct);
             mGL.mVtx.type = c.getGLType();
+            mGL.mVtx.normalized = false;
+            mGL.mVtx.name.setTo("Position");
             break;
 
         case RS_KIND_COLOR:
@@ -152,6 +161,8 @@
             mGL.mColor.size = c.getVectorSize();
             mGL.mColor.offset = mElement->getFieldOffsetBytes(ct);
             mGL.mColor.type = c.getGLType();
+            mGL.mColor.normalized = c.getType() != RS_TYPE_FLOAT_32;
+            mGL.mColor.name.setTo("Color");
             break;
 
         case RS_KIND_NORMAL:
@@ -159,15 +170,17 @@
             mGL.mNorm.size = c.getVectorSize();
             mGL.mNorm.offset = mElement->getFieldOffsetBytes(ct);
             mGL.mNorm.type = c.getGLType();
+            mGL.mNorm.normalized = false;
+            mGL.mNorm.name.setTo("Normal");
             break;
 
         case RS_KIND_TEXTURE:
-            if (mGL.mTex[texNum].size) {
-                texNum++;
-            }
-            mGL.mTex[texNum].size = c.getVectorSize();
-            mGL.mTex[texNum].offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mTex[texNum].type = c.getGLType();
+            rsAssert(mGL.mTex.size == 0);
+            mGL.mTex.size = c.getVectorSize();
+            mGL.mTex.offset = mElement->getFieldOffsetBytes(ct);
+            mGL.mTex.type = c.getGLType();
+            mGL.mTex.normalized = false;
+            mGL.mTex.name.setTo("Texture");
             break;
 
         case RS_KIND_POINT_SIZE:
@@ -175,6 +188,8 @@
             mGL.mPointSize.size = c.getVectorSize();
             mGL.mPointSize.offset = mElement->getFieldOffsetBytes(ct);
             mGL.mPointSize.type = c.getGLType();
+            mGL.mPointSize.normalized = false;
+            mGL.mPointSize.name.setTo("PointSize");
         break;
 
         default:
@@ -186,7 +201,7 @@
 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
+    // here.  The reason is more than one Allocation may be used as a vertex
     // source.  So we cannot disable arrays that may have been in use by
     // another allocation.
 
@@ -211,14 +226,11 @@
                      mGL.mColor.offset);
     }
 
-    for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
-        if (mGL.mTex[ct].size) {
-            va->setTexture(mGL.mTex[ct].size,
-                           mGL.mTex[ct].type,
-                           stride,
-                           mGL.mTex[ct].offset,
-                           ct);
-        }
+    if (mGL.mTex.size) {
+        va->setTexture(mGL.mTex.size,
+                       mGL.mTex.type,
+                       stride,
+                       mGL.mTex.offset);
     }
 
     if (mGL.mPointSize.size) {
@@ -229,6 +241,20 @@
 
 }
 
+void Type::enableGLVertexBuffer2(VertexArray *va) const
+{
+    // Do legacy buffers
+    enableGLVertexBuffer(va);
+
+    uint32_t stride = mElement->getSizeBytes();
+    for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) {
+        if (mGL.mUser[ct].size) {
+            va->setUser(mGL.mUser[ct], stride);
+        }
+    }
+}
+
+
 
 void Type::dumpLOGV(const char *prefix) const
 {
diff --git a/rsType.h b/rsType.h
index d261d58..4fa4933 100644
--- a/rsType.h
+++ b/rsType.h
@@ -18,6 +18,7 @@
 #define ANDROID_STRUCTURED_TYPE_H
 
 #include "rsElement.h"
+#include "rsVertexArray.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -68,6 +69,7 @@
     void compute();
 
     void enableGLVertexBuffer(class VertexArray *) const;
+    void enableGLVertexBuffer2(class VertexArray *) const;
 
     void dumpLOGV(const char *prefix) const;
 
@@ -108,18 +110,13 @@
     LOD *mLODs;
     uint32_t mLODCount;
 
-    struct VertexComponent_t {
-        uint32_t offset;
-        uint32_t type;
-        uint32_t size;
-        uint32_t stride;
-    };
     struct GLState_t {
-        VertexComponent_t mVtx;
-        VertexComponent_t mNorm;
-        VertexComponent_t mColor;
-        VertexComponent_t mTex[RS_MAX_TEXTURE];
-        VertexComponent_t mPointSize;
+        VertexArray::Attrib mUser[RS_MAX_ATTRIBS];
+        VertexArray::Attrib mVtx;
+        VertexArray::Attrib mNorm;
+        VertexArray::Attrib mColor;
+        VertexArray::Attrib mTex;
+        VertexArray::Attrib mPointSize;
     };
     GLState_t mGL;
     void makeGLComponents();
diff --git a/rsVertexArray.cpp b/rsVertexArray.cpp
index 2ba0ef9..7a8e054 100644
--- a/rsVertexArray.cpp
+++ b/rsVertexArray.cpp
@@ -25,8 +25,8 @@
 
 VertexArray::VertexArray()
 {
-    memset(mAttribs, 0, sizeof(mAttribs));
     mActiveBuffer = 0;
+    mUserCount = 0;
 }
 
 VertexArray::~VertexArray()
@@ -36,13 +36,43 @@
 
 void VertexArray::clearAll()
 {
-    memset(mAttribs, 0, sizeof(mAttribs));
+    for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) {
+        mAttribs[ct].clear();
+    }
     mActiveBuffer = 0;
+    mUserCount = 0;
+}
+
+VertexArray::Attrib::Attrib()
+{
+    clear();
+}
+
+void VertexArray::Attrib::set(const Attrib &a)
+{
+    buffer = a.buffer;
+    offset = a.offset;
+    type = a.type;
+    size = a.size;
+    stride = a.stride;
+    normalized = a.normalized;
+    name.setTo(a.name);
+}
+
+void VertexArray::Attrib::clear()
+{
+    buffer = 0;
+    offset = 0;
+    type = 0;
+    size = 0;
+    stride = 0;
+    normalized = false;
+    name.setTo("");
 }
 
 void VertexArray::clear(AttribName n)
 {
-    mAttribs[n].size = 0;
+    mAttribs[n].clear();
 }
 
 void VertexArray::setPosition(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset)
@@ -85,18 +115,27 @@
     mAttribs[POINT_SIZE].normalized = false;
 }
 
-void VertexArray::setTexture(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset, uint32_t num)
+void VertexArray::setTexture(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset)
 {
-    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;
+    mAttribs[TEXTURE].buffer = mActiveBuffer;
+    mAttribs[TEXTURE].type = type;
+    mAttribs[TEXTURE].size = size;
+    mAttribs[TEXTURE].offset = offset;
+    mAttribs[TEXTURE].stride = stride;
+    mAttribs[TEXTURE].normalized = false;
 }
 
-void VertexArray::logAttrib(uint32_t idx) const {
-    LOGE("va %i: buf=%i  size=%i  type=0x%x  stride=0x%x  norm=%i  offset=0x%x", idx,
+void VertexArray::setUser(const Attrib &a, uint32_t stride)
+{
+    mAttribs[mUserCount].set(a);
+    mAttribs[mUserCount].buffer = mActiveBuffer;
+    mAttribs[mUserCount].stride = stride;
+    mUserCount ++;
+}
+
+void VertexArray::logAttrib(uint32_t idx, uint32_t slot) const {
+    LOGE("va %i: slot=%i name=%s buf=%i  size=%i  type=0x%x  stride=0x%x  norm=%i  offset=0x%x", idx, slot,
+         mAttribs[idx].name.string(),
          mAttribs[idx].buffer,
          mAttribs[idx].size,
          mAttribs[idx].type,
@@ -143,21 +182,18 @@
         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[TEXTURE].size) {
+        //logAttrib(TEXTURE);
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        glBindBuffer(GL_ARRAY_BUFFER, mAttribs[TEXTURE].buffer);
+        glTexCoordPointer(mAttribs[TEXTURE].size,
+                          mAttribs[TEXTURE].type,
+                          mAttribs[TEXTURE].stride,
+                          (void *)mAttribs[TEXTURE].offset);
+    } else {
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    }
 
     if (mAttribs[POINT_SIZE].size) {
         //logAttrib(POINT_SIZE);
@@ -178,12 +214,12 @@
         glDisableVertexAttribArray(ct);
     }
 
-    for (int ct=0; ct < _LAST; ct++) {
+    for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) {
         if (mAttribs[ct].size) {
-            //logAttrib(ct);
+            //logAttrib(ct, sc->vtxAttribSlot(ct));
+            rsAssert(sc->vtxAttribSlot(ct) >= 0);
             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,
@@ -191,9 +227,6 @@
                                   mAttribs[ct].normalized,
                                   mAttribs[ct].stride,
                                   (void *)mAttribs[ct].offset);
-        } else {
-            //glDisableVertexAttribArray(ct);
-            rsAssert(ct);
         }
     }
     rsc->checkError("VertexArray::setupGL2");
@@ -201,6 +234,5 @@
 ////////////////////////////////////////////
 
 void VertexArrayState::init(Context *) {
-    memset(this, 0, sizeof(this));
 }
 
diff --git a/rsVertexArray.h b/rsVertexArray.h
index f97813f..998e9ad 100644
--- a/rsVertexArray.h
+++ b/rsVertexArray.h
@@ -38,19 +38,24 @@
         COLOR,
         NORMAL,
         POINT_SIZE,
-        TEXTURE_0,
-        TEXTURE_1,
+        TEXTURE,
         _LAST
     };
 
-    typedef struct {
+    class Attrib {
+    public:
         uint32_t buffer;
         uint32_t offset;
         uint32_t type;
         uint32_t size;
         uint32_t stride;
         bool normalized;
-    } Attrib;
+        String8 name;
+
+        Attrib();
+        void set(const Attrib &);
+        void clear();
+    };
 
 
     void clearAll();
@@ -58,19 +63,21 @@
 
     void setActiveBuffer(uint32_t id) {mActiveBuffer = id;}
 
+    void setUser(const Attrib &, uint32_t stride);
     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 setTexture(uint32_t size, uint32_t type, uint32_t stride, uint32_t offset);
 
     void setupGL(const Context *rsc, class VertexArrayState *) const;
     void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const;
-    void logAttrib(uint32_t idx) const;
+    void logAttrib(uint32_t idx, uint32_t slot) const;
 
 protected:
     uint32_t mActiveBuffer;
-    Attrib mAttribs[_LAST];
+    uint32_t mUserCount;
+    Attrib mAttribs[RS_MAX_ATTRIBS];
 };
 
 
@@ -78,7 +85,7 @@
 public:
     void init(Context *);
 
-    VertexArray::Attrib mAttribs[VertexArray::_LAST];
+    //VertexArray::Attrib mAttribs[VertexArray::_LAST];
 };