Support for cubemaps.

Change-Id: Iaf6087f614451a8e233b3e5bc49c834ab0ad08ee
diff --git a/RenderScript.h b/RenderScript.h
index 20e289d..0ed129f 100644
--- a/RenderScript.h
+++ b/RenderScript.h
@@ -160,6 +160,11 @@
     RS_SAMPLER_CLAMP
 };
 
+enum RsTextureTarget {
+    RS_TEXTURE_2D,
+    RS_TEXTURE_CUBE
+};
+
 enum RsDimension {
     RS_DIMENSION_X,
     RS_DIMENSION_Y,
@@ -218,7 +223,7 @@
     RS_PROGRAM_PARAM_INPUT,
     RS_PROGRAM_PARAM_OUTPUT,
     RS_PROGRAM_PARAM_CONSTANT,
-    RS_PROGRAM_PARAM_TEXTURE_COUNT,
+    RS_PROGRAM_PARAM_TEXTURE_TYPE,
 };
 
 enum RsPrimitive {
@@ -322,6 +327,7 @@
                      const RsDimension *dims, const uint32_t *vals);
 RsAllocation rsaAllocationCreateTyped(RsContext rsc, RsType vtype);
 RsAllocation rsaAllocationCreateFromBitmap(RsContext con, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data);
+RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data);
 
 #ifndef NO_RS_FUNCS
 #include "rsgApiFuncDecl.h"
diff --git a/java/Samples/res/drawable/cubemap_test.png b/java/Samples/res/drawable/cubemap_test.png
new file mode 100644
index 0000000..75ad0a4
--- /dev/null
+++ b/java/Samples/res/drawable/cubemap_test.png
Binary files differ
diff --git a/java/Samples/res/raw/shadercubef.glsl b/java/Samples/res/raw/shadercubef.glsl
new file mode 100644
index 0000000..15696a4
--- /dev/null
+++ b/java/Samples/res/raw/shadercubef.glsl
@@ -0,0 +1,8 @@
+
+varying vec3 worldNormal;
+
+void main() {
+   lowp vec4 col = textureCube(UNI_Tex0, worldNormal);
+   gl_FragColor = col;
+}
+
diff --git a/java/Samples/res/raw/shadercubev.glsl b/java/Samples/res/raw/shadercubev.glsl
new file mode 100644
index 0000000..70f5cd6
--- /dev/null
+++ b/java/Samples/res/raw/shadercubev.glsl
@@ -0,0 +1,10 @@
+varying vec3 worldNormal;
+
+// This is where actual shader code begins
+void main() {
+   vec4 worldPos = UNI_model * ATTRIB_position;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   worldNormal = model3 * ATTRIB_normal;
+}
diff --git a/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index f0b69d1..766601b 100644
--- a/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -22,6 +22,8 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.renderscript.*;
+import android.renderscript.Allocation.CubemapLayout;
+import android.renderscript.Program.TextureType;
 import android.renderscript.ProgramStore.DepthFunc;
 import android.renderscript.Sampler.Value;
 import android.util.Log;
@@ -80,6 +82,9 @@
     private ProgramVertex mProgVertexCustom2;
     private ProgramFragment mProgFragmentCustom2;
 
+    private ProgramVertex mProgVertexCube;
+    private ProgramFragment mProgFragmentCube;
+
     private ProgramRaster mCullBack;
     private ProgramRaster mCullFront;
     private ProgramRaster mCullNone;
@@ -88,6 +93,7 @@
     private Allocation mTexOpaque;
     private Allocation mTexTransparent;
     private Allocation mTexChecker;
+    private Allocation mTexCube;
 
     private Mesh mMbyNMesh;
     private Mesh mTorus;
@@ -240,6 +246,19 @@
         mProgFragmentCustom2 = pfbCustom.create();
         mProgFragmentCustom2.bindConstants(mFSConst2.getAllocation(), 0);
 
+        // Cubemap test shaders
+        pvbCustom = new ProgramVertex.ShaderBuilder(mRS);
+        pvbCustom.setShader(mRes, R.raw.shadercubev);
+        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
+        pvbCustom.addConstant(mVSConst.getAllocation().getType());
+        mProgVertexCube = pvbCustom.create();
+        mProgVertexCube.bindConstants(mVSConst.getAllocation(), 0);
+
+        pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
+        pfbCustom.setShader(mRes, R.raw.shadercubef);
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_CUBE);
+        mProgFragmentCube = pfbCustom.create();
+
         pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
         pfbCustom.setShader(mRes, R.raw.multitexf);
         pfbCustom.setTextureCount(3);
@@ -247,10 +266,11 @@
 
         mScript.set_gProgVertexCustom(mProgVertexCustom);
         mScript.set_gProgFragmentCustom(mProgFragmentCustom);
-        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
-
         mScript.set_gProgVertexCustom2(mProgVertexCustom2);
         mScript.set_gProgFragmentCustom2(mProgFragmentCustom2);
+        mScript.set_gProgVertexCube(mProgVertexCube);
+        mScript.set_gProgFragmentCube(mProgFragmentCube);
+        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
     }
 
     private Allocation loadTextureRGB(int id) {
@@ -272,11 +292,16 @@
         mTexOpaque = loadTextureRGB(R.drawable.data);
         mTexTransparent = loadTextureARGB(R.drawable.leaf);
         mTexChecker = loadTextureRGB(R.drawable.checker);
+        Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test);
+        mTexCube = Allocation.createCubemapFromBitmap(mRS, b, Element.RGB_565(mRS), false,
+                                                      Allocation.CubemapLayout.VERTICAL_FACE_LIST);
+        mTexCube.uploadToTexture(0);
 
         mScript.set_gTexTorus(mTexTorus);
         mScript.set_gTexOpaque(mTexOpaque);
         mScript.set_gTexTransparent(mTexTransparent);
         mScript.set_gTexChecker(mTexChecker);
+        mScript.set_gTexCube(mTexCube);
     }
 
     private void initFonts() {
diff --git a/java/Samples/src/com/android/samples/rsrenderstates.rs b/java/Samples/src/com/android/samples/rsrenderstates.rs
index f26633d..39b0834 100644
--- a/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -19,7 +19,7 @@
 #include "rs_graphics.rsh"
 #include "shader_def.rsh"
 
-const int gMaxModes = 10;
+const int gMaxModes = 11;
 
 rs_program_vertex gProgVertex;
 rs_program_fragment gProgFragmentColor;
@@ -34,6 +34,7 @@
 rs_allocation gTexTorus;
 rs_allocation gTexTransparent;
 rs_allocation gTexChecker;
+rs_allocation gTexCube;
 
 rs_mesh gMbyNMesh;
 rs_mesh gTorusMesh;
@@ -71,6 +72,8 @@
 rs_program_fragment gProgFragmentCustom;
 rs_program_vertex gProgVertexCustom2;
 rs_program_fragment gProgFragmentCustom2;
+rs_program_vertex gProgVertexCube;
+rs_program_fragment gProgFragmentCube;
 rs_program_fragment gProgFragmentMultitex;
 
 float gDt = 0;
@@ -506,6 +509,42 @@
     rsgDrawText("Custom shader sample with array uniforms", 10, rsgGetHeight() - 10);
 }
 
+void displayCubemapShaderSample() {
+    // Update vertex shader constants
+    // Load model matrix
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Position our model on the screen
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsAllocationMarkDirty(rsGetAllocation(gFSConstants));
+
+    rsgBindProgramVertex(gProgVertexCube);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCube);
+    rsgBindSampler(gProgFragmentCube, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCube, 0, gTexCube);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Cubemap shader sample", 10, rsgGetHeight() - 10);
+}
+
 void displayMultitextureSample() {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
@@ -632,6 +671,9 @@
     case 9:
         displayCustomShaderSamples2();
         break;
+    case 10:
+        displayCubemapShaderSample();
+        break;
     }
 
     return 10;
diff --git a/rsAdapter.cpp b/rsAdapter.cpp
index 2a705a1..8d363fd 100644
--- a/rsAdapter.cpp
+++ b/rsAdapter.cpp
@@ -143,8 +143,19 @@
     rsAssert(mAllocation.get());
     rsAssert(mAllocation->getPtr());
     rsAssert(mAllocation->getType());
+    if (mFace != 0 && !mAllocation->getType()->getDimFaces()) {
+        LOGE("Adapter wants cubemap face, but allocation has none");
+        return NULL;
+    }
+
     uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr());
     ptr += mAllocation->getType()->getLODOffset(mLOD, x, y);
+
+    if (mFace != 0) {
+        uint32_t totalSizeBytes = mAllocation->getType()->getSizeBytes();
+        uint32_t faceOffset = totalSizeBytes / 6;
+        ptr += faceOffset * mFace;
+    }
     return ptr;
 }
 
diff --git a/rsAllocation.cpp b/rsAllocation.cpp
index 23135e2..4ade714 100644
--- a/rsAllocation.cpp
+++ b/rsAllocation.cpp
@@ -123,8 +123,22 @@
     mTextureGenMipmap = !mType->getDimLOD() && genMipmap;
 }
 
+uint32_t Allocation::getGLTarget() const {
+    if (mIsTexture) {
+        if (mType->getDimFaces()) {
+            return GL_TEXTURE_CUBE_MAP;
+        } else {
+            return GL_TEXTURE_2D;
+        }
+    }
+    if (mIsVertexBuffer) {
+        return GL_ARRAY_BUFFER;
+    }
+    return 0;
+}
+
+
 void Allocation::uploadToTexture(const Context *rsc) {
-    //rsAssert(!mTextureId);
 
     mIsTexture = true;
     if (!rsc->checkDriver()) {
@@ -155,9 +169,30 @@
         }
         isFirstUpload = true;
     }
-    glBindTexture(GL_TEXTURE_2D, mTextureID);
+
+    GLenum target = (GLenum)getGLTarget();
+    glBindTexture(target, mTextureID);
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
+    if (target == GL_TEXTURE_2D) {
+        upload2DTexture(isFirstUpload);
+    } else if (target == GL_TEXTURE_CUBE_MAP) {
+        uploadCubeTexture(isFirstUpload);
+    }
+
+    if (mTextureGenMipmap) {
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+        glGenerateMipmap(target);
+#endif //ANDROID_RS_BUILD_FOR_HOST
+    }
+
+    rsc->checkError("Allocation::uploadToTexture");
+}
+
+void Allocation::upload2DTexture(bool isFirstUpload) {
+    GLenum type = mType->getElement()->getComponent().getGLType();
+    GLenum format = mType->getElement()->getComponent().getGLFormat();
+
     Adapter2D adapt(getContext(), this);
     for (uint32_t lod = 0; (lod + mTextureLOD) < mType->getLODCount(); lod++) {
         adapt.setLOD(lod+mTextureLOD);
@@ -169,17 +204,45 @@
                          0, format, type, ptr);
         } else {
             glTexSubImage2D(GL_TEXTURE_2D, lod, 0, 0,
-                         adapt.getDimX(), adapt.getDimY(),
-                         format, type, ptr);
+                            adapt.getDimX(), adapt.getDimY(),
+                            format, type, ptr);
         }
     }
-    if (mTextureGenMipmap) {
-#ifndef ANDROID_RS_BUILD_FOR_HOST
-        glGenerateMipmap(GL_TEXTURE_2D);
-#endif //ANDROID_RS_BUILD_FOR_HOST
-    }
+}
 
-    rsc->checkError("Allocation::uploadToTexture");
+void Allocation::uploadCubeTexture(bool isFirstUpload) {
+    GLenum type = mType->getElement()->getComponent().getGLType();
+    GLenum format = mType->getElement()->getComponent().getGLFormat();
+
+    GLenum faceOrder[] = {
+        GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+        GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+    };
+
+    Adapter2D adapt(getContext(), this);
+    for (uint32_t face = 0; face < 6; face ++) {
+        adapt.setFace(face);
+
+        for (uint32_t lod = 0; (lod + mTextureLOD) < mType->getLODCount(); lod++) {
+            adapt.setLOD(lod+mTextureLOD);
+
+            uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0));
+
+            if (isFirstUpload) {
+                glTexImage2D(faceOrder[face], lod, format,
+                             adapt.getDimX(), adapt.getDimY(),
+                             0, format, type, ptr);
+            } else {
+                glTexSubImage2D(faceOrder[face], lod, 0, 0,
+                                adapt.getDimX(), adapt.getDimY(),
+                                format, type, ptr);
+            }
+        }
+    }
 }
 
 void Allocation::deferedUploadToBufferObject(const Context *rsc) {
@@ -205,10 +268,10 @@
         mUploadDefered = true;
         return;
     }
-
-    glBindBuffer(GL_ARRAY_BUFFER, mBufferID);
-    glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW);
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    GLenum target = (GLenum)getGLTarget();
+    glBindBuffer(target, mBufferID);
+    glBufferData(target, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW);
+    glBindBuffer(target, 0);
     rsc->checkError("Allocation::uploadToBufferObject");
 }
 
@@ -816,3 +879,55 @@
 
     return texAlloc;
 }
+
+RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data) {
+    Context *rsc = static_cast<Context *>(con);
+    const Element *src = static_cast<const Element *>(_src);
+    const Element *dst = static_cast<const Element *>(_dst);
+
+    // Cubemap allocation's faces should be Width by Width each.
+    // Source data should have 6 * Width by Width pixels
+    // Error checking is done in the java layer
+    RsDimension dims[] = {RS_DIMENSION_X, RS_DIMENSION_Y, RS_DIMENSION_LOD, RS_DIMENSION_FACE};
+    uint32_t dimValues[] = {w, w, genMips, true};
+    RsType type = rsaTypeCreate(rsc, _dst, 4, dims, dimValues);
+
+    RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, type);
+    Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+    if (texAlloc == NULL) {
+        LOGE("Memory allocation failure");
+        return NULL;
+    }
+
+    uint8_t *sourcePtr = (uint8_t*)data;
+    ElementConverter_t cvt = pickConverter(dst, src);
+    if (cvt) {
+        for (uint32_t face = 0; face < 6; face ++) {
+            Adapter2D faceAdapter(rsc, texAlloc);
+            faceAdapter.setFace(face);
+
+            cvt(faceAdapter.getElement(0, 0), sourcePtr, w * w);
+
+            // Move the data pointer to the next cube face
+            sourcePtr += w * w * src->getSizeBytes();
+
+            if (genMips) {
+                Adapter2D adapt(rsc, texAlloc);
+                Adapter2D adapt2(rsc, texAlloc);
+                adapt.setFace(face);
+                adapt2.setFace(face);
+                for (uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+                    adapt.setLOD(lod);
+                    adapt2.setLOD(lod + 1);
+                    mip(adapt2, adapt);
+                }
+            }
+        }
+    } else {
+        rsc->setError(RS_ERROR_BAD_VALUE, "Unsupported bitmap format");
+        delete texAlloc;
+        return NULL;
+    }
+
+    return texAlloc;
+}
diff --git a/rsAllocation.h b/rsAllocation.h
index f9a0fc9..5b432f2 100644
--- a/rsAllocation.h
+++ b/rsAllocation.h
@@ -48,6 +48,8 @@
     void uploadToTexture(const Context *rsc);
     uint32_t getTextureID() const {return mTextureID;}
 
+    uint32_t getGLTarget() const;
+
     void deferedUploadToBufferObject(const Context *rsc);
     void uploadToBufferObject(const Context *rsc);
     uint32_t getBufferObjectID() const {return mBufferID;}
@@ -134,7 +136,8 @@
 
 private:
     void init(Context *rsc, const Type *);
-
+    void upload2DTexture(bool isFirstUpload);
+    void uploadCubeTexture(bool isFirstUpload);
 };
 
 }
diff --git a/rsFont.cpp b/rsFont.cpp
index e4d77b2..107022d 100644
--- a/rsFont.cpp
+++ b/rsFont.cpp
@@ -498,8 +498,8 @@
     uint32_t tmp[4];
     tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
     tmp[1] = (uint32_t)inputType;
-    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
-    tmp[3] = 1;
+    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE;
+    tmp[3] = RS_TEXTURE_2D;
 
     mFontShaderFConstant.set(new Allocation(mRSC, inputType));
     ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
diff --git a/rsProgram.cpp b/rsProgram.cpp
index 1c44e71..39b85e3 100644
--- a/rsProgram.cpp
+++ b/rsProgram.cpp
@@ -48,13 +48,14 @@
         if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) {
             mConstantCount++;
         }
-        if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_COUNT) {
-            mTextureCount = params[ct+1];
+        if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) {
+            mTextureCount++;
         }
     }
 
     mTextures = new ObjectBaseRef<Allocation>[mTextureCount];
     mSamplers = new ObjectBaseRef<Sampler>[mTextureCount];
+    mTextureTargets = new RsTextureTarget[mTextureCount];
     mInputElements = new ObjectBaseRef<Element>[mInputCount];
     mOutputElements = new ObjectBaseRef<Element>[mOutputCount];
     mConstantTypes = new ObjectBaseRef<Type>[mConstantCount];
@@ -63,6 +64,7 @@
     uint32_t input = 0;
     uint32_t output = 0;
     uint32_t constant = 0;
+    uint32_t texture = 0;
     for (uint32_t ct=0; ct < paramLength; ct+=2) {
         if (params[ct] == RS_PROGRAM_PARAM_INPUT) {
             mInputElements[input++].set(reinterpret_cast<Element *>(params[ct+1]));
@@ -73,6 +75,9 @@
         if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) {
             mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
         }
+        if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) {
+            mTextureTargets[texture++] = (RsTextureTarget)params[ct+1];
+        }
     }
     mIsInternal = false;
     uint32_t internalTokenLen = strlen(RS_SHADER_INTERNAL);
@@ -106,6 +111,7 @@
     }
     delete[] mTextures;
     delete[] mSamplers;
+    delete[] mTextureTargets;
     delete[] mInputElements;
     delete[] mOutputElements;
     delete[] mConstantTypes;
@@ -127,6 +133,7 @@
 
     mTextures = NULL;
     mSamplers = NULL;
+    mTextureTargets = NULL;
     mInputElements = NULL;
     mOutputElements = NULL;
     mConstantTypes = NULL;
@@ -176,6 +183,12 @@
         return;
     }
 
+    if (a && a->getType()->getDimFaces() && mTextureTargets[slot] != RS_TEXTURE_CUBE) {
+        LOGE("Attempt to bind cubemap to slot %u but 2d texture needed", slot);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind cubemap to 2d texture slot");
+        return;
+    }
+
     //LOGE("bindtex %i %p", slot, a);
     mTextures[slot].set(a);
     mDirty = true;
diff --git a/rsProgram.h b/rsProgram.h
index b682b97..c48464d 100644
--- a/rsProgram.h
+++ b/rsProgram.h
@@ -105,7 +105,7 @@
     // Constants are strictly accessed by programetic loads.
     ObjectBaseRef<Allocation> *mTextures;
     ObjectBaseRef<Sampler> *mSamplers;
-
+    RsTextureTarget *mTextureTargets;
     bool loadShader(Context *, uint32_t type);
 };
 
diff --git a/rsProgramFragment.cpp b/rsProgramFragment.cpp
index 407522b..ffa7d26 100644
--- a/rsProgramFragment.cpp
+++ b/rsProgramFragment.cpp
@@ -99,15 +99,20 @@
         }
 
         mTextures[ct]->uploadCheck(rsc);
-        glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
+        GLenum target = (GLenum)mTextures[ct]->getGLTarget();
+        if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) {
+            LOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader");
+        }
+        glBindTexture(target, mTextures[ct]->getTextureID());
         rsc->checkError("ProgramFragment::setupGL2 tex bind");
         if (mSamplers[ct].get()) {
             mSamplers[ct]->setupGL(rsc, mTextures[ct].get());
         } 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_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
             rsc->checkError("ProgramFragment::setupGL2 tex env");
         }
 
@@ -130,7 +135,11 @@
         appendUserConstants();
         char buf[256];
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
-            sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
+            if (mTextureTargets[ct] == RS_TEXTURE_2D) {
+                sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
+            } else {
+                sprintf(buf, "uniform samplerCube UNI_Tex%i;\n", ct);
+            }
             mShader.append(buf);
         }
         mShader.append(mUserShader);
@@ -191,15 +200,13 @@
 
     Type *inputType = Type::getType(rsc, constInput, 1, 0, 0, false, false);
 
-    uint32_t tmp[4];
+    uint32_t tmp[2];
     tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
     tmp[1] = (uint32_t)inputType;
-    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
-    tmp[3] = 0;
 
     Allocation *constAlloc = new Allocation(rsc, inputType);
     ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
-                                              shaderString.length(), tmp, 4);
+                                              shaderString.length(), tmp, 2);
     pf->bindAllocation(rsc, constAlloc, 0);
     pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f);
 
diff --git a/rsProgramVertex.cpp b/rsProgramVertex.cpp
index e165967..3fd2981 100644
--- a/rsProgramVertex.cpp
+++ b/rsProgramVertex.cpp
@@ -253,16 +253,14 @@
     shaderString.append("  varTex0 = ATTRIB_texture0;\n");
     shaderString.append("}\n");
 
-    uint32_t tmp[6];
+    uint32_t tmp[4];
     tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
     tmp[1] = (uint32_t)inputType;
     tmp[2] = RS_PROGRAM_PARAM_INPUT;
     tmp[3] = (uint32_t)attrElem;
-    tmp[4] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
-    tmp[5] = 0;
 
     ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
-                                          shaderString.length(), tmp, 6);
+                                          shaderString.length(), tmp, 4);
     Allocation *alloc = new Allocation(rsc, inputType);
     pv->bindAllocation(rsc, alloc, 0);
 
diff --git a/rsSampler.cpp b/rsSampler.cpp
index 1d0d270..54282a8 100644
--- a/rsSampler.cpp
+++ b/rsSampler.cpp
@@ -71,29 +71,32 @@
         GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
     };
 
+    // This tells us the correct texture type
+    GLenum target = (GLenum)tex->getGLTarget();
+
     if (!rsc->ext_OES_texture_npot() && tex->getType()->getIsNp2()) {
         if (tex->getHasGraphicsMipmaps() && rsc->ext_GL_NV_texture_npot_2D_mipmap()) {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
         } else {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
         }
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, transNP[mMagFilter]);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, transNP[mWrapS]);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, transNP[mWrapT]);
+        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, transNP[mMagFilter]);
+        glTexParameteri(target, GL_TEXTURE_WRAP_S, transNP[mWrapS]);
+        glTexParameteri(target, GL_TEXTURE_WRAP_T, transNP[mWrapT]);
     } else {
         if (tex->getHasGraphicsMipmaps()) {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
         } else {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
         }
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
+        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
+        glTexParameteri(target, GL_TEXTURE_WRAP_S, trans[mWrapS]);
+        glTexParameteri(target, GL_TEXTURE_WRAP_T, trans[mWrapT]);
     }
 
     float anisoValue = rsMin(rsc->ext_texture_max_aniso(), mAniso);
     if (rsc->ext_texture_max_aniso() > 1.0f) {
-        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue);
+        glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue);
     }
 
     rsc->checkError("Sampler::setupGL2 tex env");