Adding support for all allowed textures.
Cleaning up unused code
Adding error messages

Change-Id: I3a92476738ff7699d49feeafcd3eee6f70621acb
diff --git a/java/Samples/res/raw/multitexf.glsl b/java/Samples/res/raw/multitexf.glsl
new file mode 100644
index 0000000..91151ad
--- /dev/null
+++ b/java/Samples/res/raw/multitexf.glsl
@@ -0,0 +1,12 @@
+varying vec4 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba;
+   lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba;
+   lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba;
+   col0.xyz = col0.xyz*col1.xyz*1.5;
+   col0.xyz = mix(col0.xyz, col2.xyz, col2.w);
+   gl_FragColor = col0;
+}
+
diff --git a/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index 18b94d9..d4e83d3 100644
--- a/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -42,7 +42,7 @@
         mOptionsARGB.inScaled = false;
         mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
         mMode = 0;
-        mMaxModes = 7;
+        mMaxModes = 8;
         initRS();
     }
 
@@ -68,6 +68,7 @@
     // Custom shaders
     private ProgramVertex mProgVertexCustom;
     private ProgramFragment mProgFragmentCustom;
+    private ProgramFragment mProgFragmentMultitex;
     private ScriptField_VertexShaderConstants_s mVSConst;
     private ScriptField_FragentShaderConstants_s mFSConst;
 
@@ -214,8 +215,14 @@
         // Bind the source of constant data
         mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
 
+        pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
+        pfbCustom.setShader(mRes, R.raw.multitexf);
+        pfbCustom.setTextureCount(3);
+        mProgFragmentMultitex = pfbCustom.create();
+
         mScript.set_gProgVertexCustom(mProgVertexCustom);
         mScript.set_gProgFragmentCustom(mProgFragmentCustom);
+        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
     }
 
     private Allocation loadTextureRGB(int id) {
diff --git a/java/Samples/src/com/android/samples/rsrenderstates.rs b/java/Samples/src/com/android/samples/rsrenderstates.rs
index c7bea93..659e1e4 100644
--- a/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -60,6 +60,7 @@
 // Custom shaders we use for lighting
 rs_program_vertex gProgVertexCustom;
 rs_program_fragment gProgFragmentCustom;
+rs_program_fragment gProgFragmentMultitex;
 
 #pragma rs export_var(gProgVertex, gProgFragmentColor, gProgFragmentTexture)
 #pragma rs export_var(gProgStoreBlendNoneDepth, gProgStoreBlendNone, gProgStoreBlendAlpha, gProgStoreBlendAdd)
@@ -68,7 +69,7 @@
 #pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono)
 #pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gNearestClamp)
 #pragma rs export_var(gCullBack, gCullFront)
-#pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom)
+#pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom, gProgFragmentMultitex)
 
 //What we are showing
 #pragma rs export_var(gDisplayMode)
@@ -414,9 +415,38 @@
 
     rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
     rsgBindFont(gFontMono);
-    //rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
+    rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
 }
 
+void displayMultitextureSample() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentMultitex);
+    rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
+    rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
+    rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
+    rsgBindTexture(gProgFragmentMultitex, 0, gTexOpaque);
+    rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
+    rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader with multitexturing", 10, 280);
+}
+
+
 int root(int launchID) {
 
     gDt = rsGetDt();
@@ -446,6 +476,9 @@
     case 6:
         displayCustomShaderSamples();
         break;
+    case 7:
+        displayMultitextureSample();
+        break;
     }
 
     return 10;
diff --git a/rs.spec b/rs.spec
index 21cbc50..2b7928f 100644
--- a/rs.spec
+++ b/rs.spec
@@ -434,12 +434,6 @@
 	}
 
 ProgramFragmentCreate {
-	param const uint32_t * params
-	param uint32_t paramLength
-	ret RsProgramFragment
-	}
-
-ProgramFragmentCreate2 {
 	param const char * shaderText
 	param uint32_t shaderLength
 	param const uint32_t * params
@@ -448,11 +442,6 @@
 	}
 
 ProgramVertexCreate {
-	param bool texMat
-	ret RsProgramVertex
-	}
-
-ProgramVertexCreate2 {
 	param const char * shaderText
 	param uint32_t shaderLength
 	param const uint32_t * params
diff --git a/rsContext.cpp b/rsContext.cpp
index 3dbdbfb..3681bc2 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -163,11 +163,6 @@
     uint32_t ret = runScript(mRootScript.get());
 
     checkError("runRootScript");
-    if (mError != RS_ERROR_NONE) {
-        // If we have an error condition we stop rendering until
-        // somthing changes that might fix it.
-        ret = 0;
-    }
     return ret;
 }
 
diff --git a/rsContext.h b/rsContext.h
index e38ba55..bce9c13 100644
--- a/rsContext.h
+++ b/rsContext.h
@@ -169,6 +169,9 @@
     mutable const ObjectBase * mObjHead;
 
     bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+    uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;}
+    uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;}
+    uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
 
     void launchThreads(WorkerCallback_t cbk, void *data);
     uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mRunningCount;}
diff --git a/rsContextHostStub.h b/rsContextHostStub.h
index c437606..f30915e 100644
--- a/rsContextHostStub.h
+++ b/rsContextHostStub.h
@@ -120,6 +120,9 @@
     mutable const ObjectBase * mObjHead;
 
     bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+    uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;}
+    uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;}
+    uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
 
 protected:
 
diff --git a/rsElement.cpp b/rsElement.cpp
index d0909c8..0b9e28c 100644
--- a/rsElement.cpp
+++ b/rsElement.cpp
@@ -138,37 +138,10 @@
     // We need to check if this already exists
     for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) {
         Element *ee = rsc->mStateElement.mElements[ct];
-
-        if (!ee->getFieldCount() ) {
-
-            if((ee->getComponent().getType() == elem->getComponent().getType()) &&
-               (ee->getComponent().getKind() == elem->getComponent().getKind()) &&
-               (ee->getComponent().getIsNormalized() == elem->getComponent().getIsNormalized()) &&
-               (ee->getComponent().getVectorSize() == elem->getComponent().getVectorSize())) {
-                // Match
-                delete elem;
-                ee->incUserRef();
-                return ee;
-            }
-
-        } else if (ee->getFieldCount() == elem->mFieldCount) {
-
-            bool match = true;
-            for (uint32_t i=0; i < elem->mFieldCount; i++) {
-                if ((ee->mFields[i].e.get() != elem->mFields[i].e.get()) ||
-                    (ee->mFields[i].name.length() != elem->mFields[i].name.length()) ||
-                    (ee->mFields[i].name != elem->mFields[i].name) ||
-                    (ee->mFields[i].arraySize != elem->mFields[i].arraySize)) {
-                    match = false;
-                    break;
-                }
-            }
-            if (match) {
-                delete elem;
-                ee->incUserRef();
-                return ee;
-            }
-
+        if(ee->isEqual(elem)) {
+            delete elem;
+            ee->incUserRef();
+            return ee;
         }
     }
 
@@ -176,6 +149,32 @@
     return elem;
 }
 
+bool Element::isEqual(const Element *other) const {
+    if(other == NULL) {
+        return false;
+    }
+    if (!other->getFieldCount() && !mFieldCount) {
+        if((other->getType() == getType()) &&
+           (other->getKind() == getKind()) &&
+           (other->getComponent().getIsNormalized() == getComponent().getIsNormalized()) &&
+           (other->getComponent().getVectorSize() == getComponent().getVectorSize())) {
+            return true;
+        }
+        return false;
+    }
+    if (other->getFieldCount() == mFieldCount) {
+        for (uint32_t i=0; i < mFieldCount; i++) {
+            if ((!other->mFields[i].e->isEqual(mFields[i].e.get())) ||
+                (other->mFields[i].name.length() != mFields[i].name.length()) ||
+                (other->mFields[i].name != mFields[i].name) ||
+                (other->mFields[i].arraySize != mFields[i].arraySize)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
 
 const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk,
                             bool isNorm, uint32_t vecSize)
diff --git a/rsElement.h b/rsElement.h
index ae6a6cc..50bca85 100644
--- a/rsElement.h
+++ b/rsElement.h
@@ -72,6 +72,8 @@
     void decRefs(const void *) const;
     bool getHasReferences() const {return mHasReference;}
 
+    bool isEqual(const Element *other) const;
+
 protected:
     // deallocate any components that are part of this element.
     void clear();
diff --git a/rsFileA3D.cpp b/rsFileA3D.cpp
index 5709f2a..893598f 100644
--- a/rsFileA3D.cpp
+++ b/rsFileA3D.cpp
@@ -68,13 +68,11 @@
     uint32_t flags = headerStream->loadU32();
     mUse64BitOffsets = (flags & 1) != 0;
 
-    LOGE("file open 64bit = %i", mUse64BitOffsets);
-
     uint32_t numIndexEntries = headerStream->loadU32();
     for(uint32_t i = 0; i < numIndexEntries; i ++) {
         A3DIndexEntry *entry = new A3DIndexEntry();
         headerStream->loadString(&entry->mObjectName);
-        LOGE("Header data, entry name = %s", entry->mObjectName.string());
+        LOGV("Header data, entry name = %s", entry->mObjectName.string());
         entry->mType = (RsA3DClassID)headerStream->loadU32();
         if(mUse64BitOffsets){
             entry->mOffset = headerStream->loadOffset();
@@ -91,7 +89,6 @@
 
 bool FileA3D::load(const void *data, size_t length)
 {
-    LOGE("Loading data. Size: %u", length);
     const uint8_t *localData = (const uint8_t *)data;
 
     size_t lengthRemaining = length;
@@ -114,8 +111,6 @@
     localData += sizeof(headerSize);
     lengthRemaining -= sizeof(headerSize);
 
-    LOGE("Loading data, headerSize = %lli", headerSize);
-
     if(lengthRemaining < headerSize) {
         return false;
     }
@@ -145,8 +140,6 @@
     localData += sizeof(mDataSize);
     lengthRemaining -= sizeof(mDataSize);
 
-    LOGE("Loading data, mDataSize = %lli", mDataSize);
-
     if(lengthRemaining < mDataSize) {
         return false;
     }
@@ -169,7 +162,7 @@
     char magicString[12];
     size_t len;
 
-    LOGE("file open 1");
+    LOGV("file open 1");
     len = fread(magicString, 1, 12, f);
     if ((len != 12) ||
         memcmp(magicString, "Android3D_ff", 12)) {
@@ -205,7 +198,7 @@
         return false;
     }
 
-    LOGE("file open size = %lli", mDataSize);
+    LOGV("file open size = %lli", mDataSize);
 
     // We should know enough to read the file in at this point.
     mAlloc = malloc(mDataSize);
@@ -220,7 +213,7 @@
 
     mReadStream = new IStream(mData, mUse64BitOffsets);
 
-    LOGE("Header is read an stream initialized");
+    LOGV("Header is read an stream initialized");
     return true;
 }
 
@@ -437,7 +430,7 @@
     }
 
     ObjectBase *obj = fa3d->initializeFromEntry(index);
-    LOGE("Returning object with name %s", obj->getName());
+    LOGV("Returning object with name %s", obj->getName());
 
     return obj;
 }
diff --git a/rsFont.cpp b/rsFont.cpp
index 0f815a2..bd5713e 100644
--- a/rsFont.cpp
+++ b/rsFont.cpp
@@ -372,7 +372,7 @@
     // This will dirty the texture and the shader so next time
     // we draw it will upload the data
     mTextTexture->deferedUploadToTexture(mRSC, false, 0);
-    mFontShaderF->bindTexture(0, mTextTexture.get());
+    mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
 
     // Some debug code
     /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
@@ -414,12 +414,12 @@
     ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
                                               shaderString.length(), tmp, 4);
     mFontShaderF.set(pf);
-    mFontShaderF->bindAllocation(mFontShaderFConstant.get(), 0);
+    mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
 
     Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
                                       RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
     mFontSampler.set(sampler);
-    mFontShaderF->bindSampler(0, sampler);
+    mFontShaderF->bindSampler(mRSC, 0, sampler);
 
     ProgramStore *fontStore = new ProgramStore(mRSC);
     mFontProgramStore.set(fontStore);
diff --git a/rsProgram.cpp b/rsProgram.cpp
index 2441491..9c66462 100644
--- a/rsProgram.cpp
+++ b/rsProgram.cpp
@@ -29,7 +29,6 @@
 using namespace android;
 using namespace android::renderscript;
 
-
 Program::Program(Context *rsc) : ObjectBase(rsc)
 {
     mAllocFile = __FILE__;
@@ -38,7 +37,10 @@
     mShaderID = 0;
     mAttribCount = 0;
     mUniformCount = 0;
+    mTextureCount = 0;
 
+    mTextures = NULL;
+    mSamplers = NULL;
     mInputElements = NULL;
     mOutputElements = NULL;
     mConstantTypes = NULL;
@@ -80,6 +82,8 @@
         }
     }
 
+    mTextures = new ObjectBaseRef<Allocation>[mTextureCount];
+    mSamplers = new ObjectBaseRef<Sampler>[mTextureCount];
     mInputElements = new ObjectBaseRef<Element>[mInputCount];
     mOutputElements = new ObjectBaseRef<Element>[mOutputCount];
     mConstantTypes = new ObjectBaseRef<Type>[mConstantCount];
@@ -112,9 +116,15 @@
 Program::~Program()
 {
     for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
-        bindAllocation(NULL, ct);
+        bindAllocation(NULL, NULL, ct);
     }
 
+    for (uint32_t ct=0; ct < mTextureCount; ct++) {
+        bindTexture(NULL, ct, NULL);
+        bindSampler(NULL, ct, NULL);
+    }
+    delete[] mTextures;
+    delete[] mSamplers;
     delete[] mInputElements;
     delete[] mOutputElements;
     delete[] mConstantTypes;
@@ -124,8 +134,22 @@
 }
 
 
-void Program::bindAllocation(Allocation *alloc, uint32_t slot)
+void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot)
 {
+    if (alloc != NULL) {
+        if (slot >= mConstantCount) {
+            LOGE("Attempt to bind alloc at slot %u, on shader id %u, but const count is %u",
+                 slot, (uint32_t)this, mConstantCount);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
+            return;
+        }
+        if (!alloc->getType()->isEqual(mConstantTypes[slot].get())) {
+            LOGE("Attempt to bind alloc at slot %u, on shader id %u, but types mismatch",
+                 slot, (uint32_t)this);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
+            return;
+        }
+    }
     if (mConstants[slot].get() == alloc) {
         return;
     }
@@ -139,10 +163,11 @@
     mDirty = true;
 }
 
-void Program::bindTexture(uint32_t slot, Allocation *a)
+void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a)
 {
-    if (slot >= MAX_TEXTURE) {
-        LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE");
+    if (slot >= mTextureCount) {
+        LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mTextureCount);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind texture");
         return;
     }
 
@@ -151,10 +176,11 @@
     mDirty = true;
 }
 
-void Program::bindSampler(uint32_t slot, Sampler *s)
+void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s)
 {
-    if (slot >= MAX_TEXTURE) {
-        LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE");
+    if (slot >= mTextureCount) {
+        LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mTextureCount);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind sampler");
         return;
     }
 
@@ -289,11 +315,13 @@
     }
 }
 
-void Program::setupUserConstants(ShaderCache *sc, bool isFragment) {
+void Program::setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment) {
     uint32_t uidx = 0;
     for (uint32_t ct=0; ct < mConstantCount; ct++) {
         Allocation *alloc = mConstants[ct].get();
         if (!alloc) {
+            LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound");
             continue;
         }
 
@@ -384,19 +412,19 @@
 void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants)
 {
     Program *p = static_cast<Program *>(vp);
-    p->bindAllocation(static_cast<Allocation *>(constants), slot);
+    p->bindAllocation(rsc, static_cast<Allocation *>(constants), slot);
 }
 
 void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a)
 {
     Program *p = static_cast<Program *>(vpf);
-    p->bindTexture(slot, static_cast<Allocation *>(a));
+    p->bindTexture(rsc, slot, static_cast<Allocation *>(a));
 }
 
 void rsi_ProgramBindSampler(Context *rsc, RsProgram vpf, uint32_t slot, RsSampler s)
 {
     Program *p = static_cast<Program *>(vpf);
-    p->bindSampler(slot, static_cast<Sampler *>(s));
+    p->bindSampler(rsc, slot, static_cast<Sampler *>(s));
 }
 
 }
diff --git a/rsProgram.h b/rsProgram.h
index e7329c2..a8f34c3 100644
--- a/rsProgram.h
+++ b/rsProgram.h
@@ -32,20 +32,19 @@
 public:
     const static uint32_t MAX_ATTRIBS = 8;
     const static uint32_t MAX_UNIFORMS = 16;
-    const static uint32_t MAX_TEXTURE = 2;
 
     Program(Context *);
     Program(Context *, const char * shaderText, uint32_t shaderLength,
                        const uint32_t * params, uint32_t paramLength);
     virtual ~Program();
 
-    void bindAllocation(Allocation *, uint32_t slot);
+    void bindAllocation(Context *, Allocation *, uint32_t slot);
     virtual void createShader();
 
     bool isUserProgram() const {return !mIsInternal;}
 
-    void bindTexture(uint32_t slot, Allocation *);
-    void bindSampler(uint32_t slot, Sampler *);
+    void bindTexture(Context *, uint32_t slot, Allocation *);
+    void bindSampler(Context *, uint32_t slot, Sampler *);
 
     uint32_t getShaderID() const {return mShaderID;}
     void setShader(const char *, uint32_t len);
@@ -75,7 +74,7 @@
 
     // Applies to vertex and fragment shaders only
     void appendUserConstants();
-    void setupUserConstants(ShaderCache *sc, bool isFragment);
+    void setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment);
     void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix);
 
     ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
@@ -97,8 +96,8 @@
     // and filtered.
     //
     // Constants are strictly accessed by programetic loads.
-    ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE];
-    ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE];
+    ObjectBaseRef<Allocation> *mTextures;
+    ObjectBaseRef<Sampler> *mSamplers;
 
     bool loadShader(Context *, uint32_t type);
 
diff --git a/rsProgramFragment.cpp b/rsProgramFragment.cpp
index 8f5c653..d511d21 100644
--- a/rsProgramFragment.cpp
+++ b/rsProgramFragment.cpp
@@ -31,40 +31,6 @@
 using namespace android;
 using namespace android::renderscript;
 
-
-ProgramFragment::ProgramFragment(Context *rsc, const uint32_t * params,
-                                 uint32_t paramLength) :
-    Program(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    rsAssert(paramLength == 6);
-
-    mConstantColor[0] = 1.f;
-    mConstantColor[1] = 1.f;
-    mConstantColor[2] = 1.f;
-    mConstantColor[3] = 1.f;
-
-    mEnvModes[0] = (RsTexEnvMode)params[0];
-    mTextureFormats[0] = params[1];
-    mEnvModes[1] = (RsTexEnvMode)params[2];
-    mTextureFormats[1] = params[3];
-    mPointSpriteEnable = params[4] != 0;
-    mVaryingColor = false;
-    if (paramLength > 5)
-        mVaryingColor = params[5] != 0;
-
-    mTextureEnableMask = 0;
-    if (mEnvModes[0]) {
-        mTextureEnableMask |= 1;
-    }
-    if (mEnvModes[1]) {
-        mTextureEnableMask |= 2;
-    }
-
-    init(rsc);
-}
-
 ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText,
                                  uint32_t shaderLength, const uint32_t * params,
                                  uint32_t paramLength) :
@@ -78,19 +44,23 @@
     mConstantColor[2] = 1.f;
     mConstantColor[3] = 1.f;
 
-    mTextureEnableMask = (1 << mTextureCount) -1;
-
     init(rsc);
 }
 
-
 ProgramFragment::~ProgramFragment()
 {
 }
 
-void ProgramFragment::setConstantColor(float r, float g, float b, float a)
+void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a)
 {
     if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation color on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot  set fixed function emulation color on user program");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation color because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Unable to set fixed function emulation color because allocation is missing");
         return;
     }
     mConstantColor[0] = r;
@@ -101,7 +71,7 @@
     mDirty = true;
 }
 
-void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
+void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
 {
     //LOGE("sgl2 frag1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
@@ -112,11 +82,22 @@
     rsc->checkError("ProgramFragment::setupGL2 start");
 
     rsc->checkError("ProgramFragment::setupGL2 begin uniforms");
-    setupUserConstants(sc, true);
+    setupUserConstants(rsc, sc, true);
 
-    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+    uint32_t numTexturesToBind = mTextureCount;
+    uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures();
+    if(numTexturesToBind >= numTexturesAvailable) {
+        LOGE("Attempting to bind %u textures on shader id %u, but only %u are available",
+             mTextureCount, (uint32_t)this, numTexturesAvailable);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available");
+        numTexturesToBind = numTexturesAvailable;
+    }
+
+    for (uint32_t ct=0; ct < numTexturesToBind; ct++) {
         glActiveTexture(GL_TEXTURE0 + ct);
-        if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
+        if (!mTextures[ct].get()) {
+            LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound");
             continue;
         }
 
@@ -151,8 +132,8 @@
     if (mUserShader.length() > 1) {
         mShader.append("precision mediump float;\n");
         appendUserConstants();
+        char buf[256];
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
-            char buf[256];
             sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
             mShader.append(buf);
         }
@@ -172,8 +153,11 @@
         }
     }
     mTextureUniformIndexStart = mUniformCount;
-    mUniformNames[mUniformCount++].setTo("UNI_Tex0");
-    mUniformNames[mUniformCount++].setTo("UNI_Tex1");
+    char buf[256];
+    for (uint32_t ct=0; ct < mTextureCount; ct++) {
+        sprintf(buf, "UNI_Tex%i", ct);
+        mUniformNames[mUniformCount++].setTo(buf);
+    }
 
     createShader();
 }
@@ -228,8 +212,8 @@
     Allocation *constAlloc = new Allocation(rsc, inputType);
     ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
                                               shaderString.length(), tmp, 4);
-    pf->bindAllocation(constAlloc, 0);
-    pf->setConstantColor(1.0f, 1.0f, 1.0f, 1.0f);
+    pf->bindAllocation(rsc, constAlloc, 0);
+    pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f);
 
     mDefault.set(pf);
 }
@@ -244,23 +228,13 @@
 namespace android {
 namespace renderscript {
 
-RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc,
-                                            const uint32_t * params,
-                                            uint32_t paramLength)
-{
-    ProgramFragment *pf = new ProgramFragment(rsc, params, paramLength);
-    pf->incUserRef();
-    //LOGE("rsi_ProgramFragmentCreate %p", pf);
-    return pf;
-}
-
-RsProgramFragment rsi_ProgramFragmentCreate2(Context *rsc, const char * shaderText,
+RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength)
 {
     ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength);
     pf->incUserRef();
-    //LOGE("rsi_ProgramFragmentCreate2 %p", pf);
+    //LOGE("rsi_ProgramFragmentCreate %p", pf);
     return pf;
 }
 
diff --git a/rsProgramFragment.h b/rsProgramFragment.h
index fb78b3f..1cf9ca7 100644
--- a/rsProgramFragment.h
+++ b/rsProgramFragment.h
@@ -28,13 +28,12 @@
 class ProgramFragment : public Program
 {
 public:
-    ProgramFragment(Context *, const uint32_t * params, uint32_t paramLength);
     ProgramFragment(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength);
     virtual ~ProgramFragment();
 
-    virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc);
+    virtual void setupGL2(Context *, ProgramFragmentState *, ShaderCache *sc);
 
     virtual void createShader();
     virtual void loadShader(Context *rsc);
@@ -43,19 +42,10 @@
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_FRAGMENT; }
     static ProgramFragment *createFromStream(Context *rsc, IStream *stream);
 
-    void setConstantColor(float, float, float, float);
+    void setConstantColor(Context *, float, float, float, float);
 
 protected:
-    // Hacks to create a program for now
-    uint32_t mTextureFormats[MAX_TEXTURE];
-    uint32_t mTextureDimensions[MAX_TEXTURE];
-    RsTexEnvMode mEnvModes[MAX_TEXTURE];
-    uint32_t mTextureEnableMask;
-    bool mPointSpriteEnable;
-    bool mVaryingColor;
-
     float mConstantColor[4];
-    int32_t mConstantColorUniformIndex;
     int32_t mTextureUniformIndexStart;
 };
 
@@ -69,7 +59,6 @@
     void init(Context *rsc);
     void deinit(Context *rsc);
 
-    ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
     ObjectBaseRef<ProgramFragment> mDefault;
     Vector<ProgramFragment *> mPrograms;
 
diff --git a/rsProgramVertex.cpp b/rsProgramVertex.cpp
index 6446b55..c3ef356 100644
--- a/rsProgramVertex.cpp
+++ b/rsProgramVertex.cpp
@@ -32,16 +32,6 @@
 using namespace android::renderscript;
 
 
-ProgramVertex::ProgramVertex(Context *rsc, bool texMat) :
-    Program(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mTextureMatrixEnable = texMat;
-    mLightCount = 0;
-    init(rsc);
-}
-
 ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength) :
@@ -49,8 +39,6 @@
 {
     mAllocFile = __FILE__;
     mAllocLine = __LINE__;
-    mTextureMatrixEnable = false;
-    mLightCount = 0;
 
     init(rsc);
 }
@@ -110,7 +98,7 @@
     }
 }
 
-void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc)
+void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc)
 {
     //LOGE("sgl2 vtx1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
@@ -132,23 +120,21 @@
     }
 
     rsc->checkError("ProgramVertex::setupGL2 begin uniforms");
-    setupUserConstants(sc, false);
+    setupUserConstants(rsc, sc, false);
 
     state->mLast.set(this);
     rsc->checkError("ProgramVertex::setupGL2");
 }
 
-void ProgramVertex::addLight(const Light *l)
-{
-    if (mLightCount < MAX_LIGHTS) {
-        mLights[mLightCount].set(l);
-        mLightCount++;
-    }
-}
-
-void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
+void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const
 {
     if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix projection on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix projection because allocation is missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -156,9 +142,16 @@
     mDirty = true;
 }
 
-void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
+void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const
 {
     if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix modelview on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix modelview because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -166,9 +159,16 @@
     mDirty = true;
 }
 
-void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
+void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const
 {
     if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix texture on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix texture because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -176,16 +176,23 @@
     mDirty = true;
 }
 
-void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const
+void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const
 {
     if(isUserProgram()) {
+        LOGE("Attempting to get fixed function emulation matrix projection on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot get emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to get fixed function emulation matrix projection because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
 }
 
-void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
+void ProgramVertex::transformToScreen(Context *rsc, float *v4out, const float *v3in) const
 {
     if(isUserProgram()) {
         return;
@@ -280,11 +287,10 @@
     ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
                                           shaderString.length(), tmp, 6);
     Allocation *alloc = new Allocation(rsc, inputType);
-    pv->bindAllocation(alloc, 0);
+    pv->bindAllocation(rsc, alloc, 0);
 
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
-    pv->bindAllocation(alloc, 0);
 
     updateSize(rsc);
 
@@ -315,15 +321,7 @@
 namespace android {
 namespace renderscript {
 
-
-RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, bool texMat)
-{
-    ProgramVertex *pv = new ProgramVertex(rsc, texMat);
-    pv->incUserRef();
-    return pv;
-}
-
-RsProgramVertex rsi_ProgramVertexCreate2(Context *rsc, const char * shaderText,
+RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength)
 {
diff --git a/rsProgramVertex.h b/rsProgramVertex.h
index 65ce541..355df2b 100644
--- a/rsProgramVertex.h
+++ b/rsProgramVertex.h
@@ -28,25 +28,18 @@
 class ProgramVertex : public Program
 {
 public:
-    const static uint32_t MAX_LIGHTS = 8;
-
     ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength,
                   const uint32_t * params, uint32_t paramLength);
-    ProgramVertex(Context *, bool texMat);
     virtual ~ProgramVertex();
 
-    virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc);
+    virtual void setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc);
 
+    void setProjectionMatrix(Context *, const rsc_Matrix *) const;
+    void getProjectionMatrix(Context *, rsc_Matrix *) const;
+    void setModelviewMatrix(Context *, const rsc_Matrix *) const;
+    void setTextureMatrix(Context *, const rsc_Matrix *) const;
 
-    void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
-    void addLight(const Light *);
-
-    void setProjectionMatrix(const rsc_Matrix *) const;
-    void getProjectionMatrix(rsc_Matrix *) const;
-    void setModelviewMatrix(const rsc_Matrix *) const;
-    void setTextureMatrix(const rsc_Matrix *) const;
-
-    void transformToScreen(const Context *, float *v4out, const float *v3in) const;
+    void transformToScreen(Context *, float *v4out, const float *v3in) const;
 
     virtual void createShader();
     virtual void loadShader(Context *);
@@ -55,13 +48,6 @@
     virtual void serialize(OStream *stream) const;
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_VERTEX; }
     static ProgramVertex *createFromStream(Context *rsc, IStream *stream);
-
-protected:
-    uint32_t mLightCount;
-    ObjectBaseRef<const Light> mLights[MAX_LIGHTS];
-
-    // Hacks to create a program for now
-    bool mTextureMatrixEnable;
 };
 
 
diff --git a/rsScriptC_LibGL.cpp b/rsScriptC_LibGL.cpp
index 2f8f79a..4be5059 100644
--- a/rsScriptC_LibGL.cpp
+++ b/rsScriptC_LibGL.cpp
@@ -93,33 +93,33 @@
 static void SC_vpLoadProjectionMatrix(const rsc_Matrix *m)
 {
     GET_TLS();
-    rsc->getVertex()->setProjectionMatrix(m);
+    rsc->getVertex()->setProjectionMatrix(rsc, m);
 }
 
 static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
 {
     GET_TLS();
-    rsc->getVertex()->setModelviewMatrix(m);
+    rsc->getVertex()->setModelviewMatrix(rsc, m);
 }
 
 static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
 {
     GET_TLS();
-    rsc->getVertex()->setTextureMatrix(m);
+    rsc->getVertex()->setTextureMatrix(rsc, m);
 }
 
 
 static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a)
 {
-    //GET_TLS();
+    GET_TLS();
     ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
-    pf->setConstantColor(r, g, b, a);
+    pf->setConstantColor(rsc, r, g, b, a);
 }
 
 static void SC_vpGetProjectionMatrix(rsc_Matrix *m)
 {
     GET_TLS();
-    rsc->getVertex()->getProjectionMatrix(m);
+    rsc->getVertex()->getProjectionMatrix(rsc, m);
 }
 
 
@@ -280,7 +280,7 @@
 {
     GET_TLS();
     ProgramFragment *pf = (ProgramFragment *)rsc->getFragment();
-    pf->setConstantColor(r, g, b, a);
+    pf->setConstantColor(rsc, r, g, b, a);
 }
 
 static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel)
diff --git a/rsType.cpp b/rsType.cpp
index 79cfd41..478cc95 100644
--- a/rsType.cpp
+++ b/rsType.cpp
@@ -254,6 +254,20 @@
     return false;
 }
 
+bool Type::isEqual(const Type *other) const {
+    if(other == NULL) {
+        return false;
+    }
+    if (other->getElement()->isEqual(getElement()) &&
+        other->getDimX() == mDimX &&
+        other->getDimY() == mDimY &&
+        other->getDimZ() == mDimZ &&
+        other->getDimLOD() == mDimLOD &&
+        other->getDimFaces() == mFaces) {
+        return true;
+    }
+    return false;
+}
 
 //////////////////////////////////////////////////
 //
diff --git a/rsType.h b/rsType.h
index 5b51e20..33faa87 100644
--- a/rsType.h
+++ b/rsType.h
@@ -77,6 +77,8 @@
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_TYPE; }
     static Type *createFromStream(Context *rsc, IStream *stream);
 
+    bool isEqual(const Type *other) const;
+
 protected:
     struct LOD {
         size_t mX;