Merge "[RenderScript] Add ScriptGroup2 API for RenderScript Support Lib."
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index e97c330..a410fde 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -774,6 +774,57 @@
         return rsnSamplerCreate(mContext, magFilter, minFilter, wrapS, wrapT, wrapR, aniso);
     }
 
+// entry points for ScriptGroup2
+    native long rsnClosureCreate(long con, long kernelID, long returnValue,
+        long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
+        long[] depFieldIDs);
+    synchronized long nClosureCreate(long kernelID, long returnValue,
+        long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
+        long[] depFieldIDs) {
+      validate();
+      return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
+          sizes, depClosures, depFieldIDs);
+    }
+
+    native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
+        long[] fieldIDs, long[] values, int[] sizes);
+    synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
+        long[] fieldIDs, long[] values, int[] sizes) {
+      validate();
+      return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+          values, sizes);
+    }
+
+    native void rsnClosureSetArg(long con, long closureID, int index,
+      long value, int size);
+    synchronized void nClosureSetArg(long closureID, int index, long value,
+        int size) {
+      validate();
+      rsnClosureSetArg(mContext, closureID, index, value, size);
+    }
+
+    native void rsnClosureSetGlobal(long con, long closureID, long fieldID,
+        long value, int size);
+    // Does this have to be synchronized?
+    synchronized void nClosureSetGlobal(long closureID, long fieldID,
+        long value, int size) {
+      validate(); // TODO: is this necessary?
+      rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
+    }
+
+    native long rsnScriptGroup2Create(long con, String cachePath, long[] closures);
+    synchronized long nScriptGroup2Create(String cachePath, long[] closures) {
+      validate();
+      return rsnScriptGroup2Create(mContext, cachePath, closures);
+    }
+
+    native void rsnScriptGroup2Execute(long con, long groupID);
+    synchronized void nScriptGroup2Execute(long groupID) {
+      validate();
+      rsnScriptGroup2Execute(mContext, groupID);
+    }
+
+
 // Additional Entry points For inc libRSSupport
 
     native boolean nIncLoadSO();
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup2.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup2.java
new file mode 100644
index 0000000..a4b67d0
--- /dev/null
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup2.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package android.support.v8.renderscript;
+
+import android.util.Log;
+import android.util.Pair;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+errors above.
+
+2) You can update current.txt by executing the following command:
+make update-api
+
+To submit the revised current.txt to the main Android repository,
+you will need approval.
+******************************
+
+@hide Pending Android public API approval.
+*/
+public class ScriptGroup2 extends BaseObj {
+    //FIXME: Change 23 to the codename when that is decided.
+    private static final int MIN_API_VERSION = 23;
+
+    public static class Closure extends BaseObj {
+        private Allocation mReturnValue;
+        private Map<Script.FieldID, Object> mBindings;
+
+        private Future mReturnFuture;
+        private Map<Script.FieldID, Future> mGlobalFuture;
+
+        private FieldPacker mFP;
+
+        private static final String TAG = "Closure";
+
+        public Closure(long id, RenderScript rs) {
+            super(id, rs);
+        }
+
+        public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
+                       Object[] args, Map<Script.FieldID, Object> globals) {
+            super(0, rs);
+
+            if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
+                throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
+            }
+
+            mReturnValue = Allocation.createTyped(rs, returnType);
+            mBindings = new HashMap<Script.FieldID, Object>();
+            mGlobalFuture = new HashMap<Script.FieldID, Future>();
+
+            int numValues = args.length + globals.size();
+
+            long[] fieldIDs = new long[numValues];
+            long[] values = new long[numValues];
+            int[] sizes = new int[numValues];
+            long[] depClosures = new long[numValues];
+            long[] depFieldIDs = new long[numValues];
+
+            int i;
+            for (i = 0; i < args.length; i++) {
+                Object obj = args[i];
+                fieldIDs[i] = 0;
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, i);
+                } else {
+                    retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes,
+                                                   depClosures, depFieldIDs);
+                }
+            }
+
+            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
+                Object obj = entry.getValue();
+                Script.FieldID fieldID = entry.getKey();
+                fieldIDs[i] = fieldID.getID(rs);
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, fieldID);
+                } else {
+                    retrieveValueAndDependenceInfo(rs, i, obj, values,
+                                                   sizes, depClosures, depFieldIDs);
+                }
+                i++;
+            }
+
+            long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
+                                        fieldIDs, values, sizes, depClosures, depFieldIDs);
+
+            setID(id);
+        }
+
+        public Closure(RenderScript rs, Script.InvokeID invokeID,
+                       Object[] args, Map<Script.FieldID, Object> globals) {
+            super(0, rs);
+
+            if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
+                throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
+            }
+
+            mFP = FieldPacker.createFieldPack(args);
+
+            mBindings = new HashMap<Script.FieldID, Object>();
+            mGlobalFuture = new HashMap<Script.FieldID, Future>();
+
+            int numValues = globals.size();
+
+            long[] fieldIDs = new long[numValues];
+            long[] values = new long[numValues];
+            int[] sizes = new int[numValues];
+            long[] depClosures = new long[numValues];
+            long[] depFieldIDs = new long[numValues];
+
+            int i = 0;
+            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
+                Object obj = entry.getValue();
+                Script.FieldID fieldID = entry.getKey();
+                fieldIDs[i] = fieldID.getID(rs);
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, fieldID);
+                } else {
+                    // TODO(yangni): Verify obj not a future.
+                    retrieveValueAndDependenceInfo(rs, i, obj, values,
+                                                   sizes, depClosures, depFieldIDs);
+                }
+                i++;
+            }
+
+            long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
+                                              values, sizes);
+
+            setID(id);
+        }
+
+        private static
+                void retrieveValueAndDependenceInfo(RenderScript rs,
+                                                    int index, Object obj,
+                                                    long[] values, int[] sizes,
+                                                    long[] depClosures,
+                                                    long[] depFieldIDs) {
+
+            if (obj instanceof Future) {
+                Future f = (Future)obj;
+                obj = f.getValue();
+                depClosures[index] = f.getClosure().getID(rs);
+                Script.FieldID fieldID = f.getFieldID();
+                depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
+                if (obj == null) {
+                    // Value is originally created by the owner closure
+                    values[index] = 0;
+                    sizes[index] = 0;
+                    return;
+                }
+            } else {
+                depClosures[index] = 0;
+                depFieldIDs[index] = 0;
+            }
+
+            ValueAndSize vs = new ValueAndSize(rs, obj);
+            values[index] = vs.value;
+            sizes[index] = vs.size;
+        }
+
+        public Future getReturn() {
+            if (mReturnFuture == null) {
+                mReturnFuture = new Future(this, null, mReturnValue);
+            }
+
+            return mReturnFuture;
+        }
+
+        public Future getGlobal(Script.FieldID field) {
+            Future f = mGlobalFuture.get(field);
+
+            if (f == null) {
+                // If the field is not bound to this closure, this will return a future
+                // without an associated value (reference). So this is not working for
+                // cross-module (cross-script) linking in this case where a field not
+                // explicitly bound.
+                f = new Future(this, field, mBindings.get(field));
+                mGlobalFuture.put(field, f);
+            }
+
+            return f;
+        }
+
+        void setArg(int index, Object obj) {
+            ValueAndSize vs = new ValueAndSize(mRS, obj);
+            mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
+        }
+
+        void setGlobal(Script.FieldID fieldID, Object obj) {
+            ValueAndSize vs = new ValueAndSize(mRS, obj);
+            mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
+        }
+
+        private static final class ValueAndSize {
+            public ValueAndSize(RenderScript rs, Object obj) {
+                if (obj instanceof Allocation) {
+                    value = ((Allocation)obj).getID(rs);
+                    size = -1;
+                } else if (obj instanceof Boolean) {
+                    value = ((Boolean)obj).booleanValue() ? 1 : 0;
+                    size = 4;
+                } else if (obj instanceof Integer) {
+                    value = ((Integer)obj).longValue();
+                    size = 4;
+                } else if (obj instanceof Long) {
+                    value = ((Long)obj).longValue();
+                    size = 8;
+                } else if (obj instanceof Float) {
+                    value = ((Float)obj).longValue();
+                    size = 4;
+                } else if (obj instanceof Double) {
+                    value = ((Double)obj).longValue();
+                    size = 8;
+                }
+            }
+            public long value;
+            public int size;
+        }
+    }
+
+    public static class Future {
+        Closure mClosure;
+        Script.FieldID mFieldID;
+        Object mValue;
+
+        Future(Closure closure, Script.FieldID fieldID, Object value) {
+            mClosure = closure;
+            mFieldID = fieldID;
+            mValue = value;
+        }
+
+        Closure getClosure() { return mClosure; }
+        Script.FieldID getFieldID() { return mFieldID; }
+        Object getValue() { return mValue; }
+    }
+
+    public static class UnboundValue {
+        // Either mFieldID or mArgIndex should be set but not both.
+        List<Pair<Closure, Script.FieldID>> mFieldID;
+        // -1 means unset. Legal values are 0 .. n-1, where n is the number of
+        // arguments for the referencing closure.
+        List<Pair<Closure, Integer>> mArgIndex;
+
+        UnboundValue() {
+            mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
+            mArgIndex = new ArrayList<Pair<Closure, Integer>>();
+        }
+
+        void addReference(Closure closure, int index) {
+            mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
+        }
+
+        void addReference(Closure closure, Script.FieldID fieldID) {
+            mFieldID.add(Pair.create(closure, fieldID));
+        }
+
+        void set(Object value) {
+            for (Pair<Closure, Integer> p : mArgIndex) {
+                Closure closure = p.first;
+                int index = p.second.intValue();
+                closure.setArg(index, value);
+            }
+            for (Pair<Closure, Script.FieldID> p : mFieldID) {
+                Closure closure = p.first;
+                Script.FieldID fieldID = p.second;
+                closure.setGlobal(fieldID, value);
+            }
+        }
+    }
+
+    List<Closure> mClosures;
+    List<UnboundValue> mInputs;
+    Future[] mOutputs;
+
+    private static final String TAG = "ScriptGroup2";
+
+    public ScriptGroup2(long id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    ScriptGroup2(RenderScript rs, List<Closure> closures,
+                 List<UnboundValue> inputs, Future[] outputs) {
+        super(0, rs);
+
+        if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
+            throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
+        }
+
+        mClosures = closures;
+        mInputs = inputs;
+        mOutputs = outputs;
+
+        long[] closureIDs = new long[closures.size()];
+        for (int i = 0; i < closureIDs.length; i++) {
+            closureIDs[i] = closures.get(i).getID(rs);
+        }
+        String cachePath = rs.getApplicationContext().getCacheDir().toString();
+        long id = rs.nScriptGroup2Create(cachePath, closureIDs);
+        setID(id);
+    }
+
+    public Object[] execute(Object... inputs) {
+        if (inputs.length < mInputs.size()) {
+            Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
+                  "less than expected " + mInputs.size());
+            return null;
+        }
+
+        if (inputs.length > mInputs.size()) {
+            Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
+                  "more than expected " + mInputs.size());
+        }
+
+        for (int i = 0; i < mInputs.size(); i++) {
+            Object obj = inputs[i];
+            if (obj instanceof Future || obj instanceof UnboundValue) {
+                Log.e(TAG, this.toString() + ": input " + i +
+                      " is a future or unbound value");
+                return null;
+            }
+            UnboundValue unbound = mInputs.get(i);
+            unbound.set(obj);
+        }
+
+        mRS.nScriptGroup2Execute(getID(mRS));
+
+        Object[] outputObjs = new Object[mOutputs.length];
+        int i = 0;
+        for (Future f : mOutputs) {
+            outputObjs[i++] = f.getValue();
+        }
+        return outputObjs;
+    }
+
+    /**
+       @hide Pending Android public API approval.
+    */
+    public static final class Binding {
+        public Script.FieldID mField;
+        public Object mValue;
+        public Binding(Script.FieldID field, Object value) {
+            mField = field;
+            mValue = value;
+        }
+    }
+
+    /**
+       @hide Pending Android public API approval.
+    */
+    public static final class Builder {
+        RenderScript mRS;
+        List<Closure> mClosures;
+        List<UnboundValue> mInputs;
+        private static final String TAG = "ScriptGroup2.Builder";
+
+        public Builder(RenderScript rs) {
+            mRS = rs;
+            mClosures = new ArrayList<Closure>();
+            mInputs = new ArrayList<UnboundValue>();
+        }
+
+        public Closure addKernel(Script.KernelID k, Type returnType, Object[] args,
+                                 Map<Script.FieldID, Object> globalBindings) {
+            Closure c = new Closure(mRS, k, returnType, args, globalBindings);
+            mClosures.add(c);
+            return c;
+        }
+
+        public Closure addInvoke(Script.InvokeID invoke, Object[] args,
+                                 Map<Script.FieldID, Object> globalBindings) {
+            Closure c = new Closure(mRS, invoke, args, globalBindings);
+            mClosures.add(c);
+            return c;
+        }
+
+        public UnboundValue addInput() {
+            UnboundValue unbound = new UnboundValue();
+            mInputs.add(unbound);
+            return unbound;
+        }
+
+        public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
+            ArrayList<Object> args = new ArrayList<Object>();
+            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
+            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
+                return null;
+            }
+            return addKernel(k, returnType, args.toArray(), bindingMap);
+        }
+
+        public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
+            ArrayList<Object> args = new ArrayList<Object>();
+            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
+            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
+                return null;
+            }
+            return addInvoke(invoke, args.toArray(), bindingMap);
+        }
+
+        public ScriptGroup2 create(Future... outputs) {
+            ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
+            return ret;
+        }
+
+        private boolean seperateArgsAndBindings(Object[] argsAndBindings,
+                                                ArrayList<Object> args,
+                                                Map<Script.FieldID, Object> bindingMap) {
+            int i;
+            for (i = 0; i < argsAndBindings.length; i++) {
+                if (argsAndBindings[i] instanceof Binding) {
+                    break;
+                }
+                args.add(argsAndBindings[i]);
+            }
+
+            for (; i < argsAndBindings.length; i++) {
+                if (!(argsAndBindings[i] instanceof Binding)) {
+                    return false;
+                }
+                Binding b = (Binding)argsAndBindings[i];
+                bindingMap.put(b.mField, b.mValue);
+            }
+
+            return true;
+        }
+
+    }
+}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java b/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
index 94ff06f..2fd894b 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
@@ -200,6 +200,81 @@
     }
 
     /**
+     * Utility function for creating basic 1D types. The type is
+     * created without mipmaps enabled.
+     *
+     * @param rs The RenderScript context
+     * @param e The Element for the Type
+     * @param dimX The X dimension, must be > 0
+     *
+     * @return Type
+     */
+    static public Type createX(RenderScript rs, Element e, int dimX) {
+        if (dimX < 1) {
+            throw new RSInvalidStateException("Dimension must be >= 1.");
+        }
+
+        long id = rs.nTypeCreate(e.getID(rs), dimX, 0, 0, false, false, 0);
+        Type t = new Type(id, rs);
+        t.mElement = e;
+        t.mDimX = dimX;
+        t.calcElementCount();
+        return t;
+    }
+
+    /**
+     * Utility function for creating basic 2D types. The type is
+     * created without mipmaps or cubemaps.
+     *
+     * @param rs The RenderScript context
+     * @param e The Element for the Type
+     * @param dimX The X dimension, must be > 0
+     * @param dimY The Y dimension, must be > 0
+     *
+     * @return Type
+     */
+    static public Type createXY(RenderScript rs, Element e, int dimX, int dimY) {
+        if ((dimX < 1) || (dimY < 1)) {
+            throw new RSInvalidStateException("Dimension must be >= 1.");
+        }
+
+        long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, 0, false, false, 0);
+        Type t = new Type(id, rs);
+        t.mElement = e;
+        t.mDimX = dimX;
+        t.mDimY = dimY;
+        t.calcElementCount();
+        return t;
+    }
+
+    /**
+     * Utility function for creating basic 3D types. The type is
+     * created without mipmaps.
+     *
+     * @param rs The RenderScript context
+     * @param e The Element for the Type
+     * @param dimX The X dimension, must be > 0
+     * @param dimY The Y dimension, must be > 0
+     * @param dimZ The Z dimension, must be > 0
+     *
+     * @return Type
+     */
+    static public Type createXYZ(RenderScript rs, Element e, int dimX, int dimY, int dimZ) {
+        if ((dimX < 1) || (dimY < 1) || (dimZ < 1)) {
+            throw new RSInvalidStateException("Dimension must be >= 1.");
+        }
+
+        long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, dimZ, false, false, 0);
+        Type t = new Type(id, rs);
+        t.mElement = e;
+        t.mDimX = dimX;
+        t.mDimY = dimY;
+        t.mDimZ = dimZ;
+        t.calcElementCount();
+        return t;
+    }
+
+    /**
      * Builder class for Type.
      *
      */
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index 38e5676..87e54f1 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -345,7 +345,6 @@
                jlong returnValue, jlongArray fieldIDArray,
                jlongArray valueArray, jintArray sizeArray,
                jlongArray depClosureArray, jlongArray depFieldIDArray) {
-  LOG_API("nClosureCreate: con(%p)", con);
   jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
   jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
   RsScriptFieldID* fieldIDs =
@@ -390,24 +389,56 @@
       depFieldIDs, (size_t)depFieldIDs_length);
 }
 
+static jlong
+nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
+                     jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
+                     jintArray sizeArray) {
+  jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
+  jsize jParamLength = _env->GetArrayLength(paramArray);
+
+  jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
+  jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+  RsScriptFieldID* fieldIDs =
+      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
+  for (int i = 0; i< fieldIDs_length; i++) {
+    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+  }
+
+  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+  jsize values_length = _env->GetArrayLength(valueArray);
+  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
+  for (int i = 0; i < values_length; i++) {
+    values[i] = (uintptr_t)jValues[i];
+  }
+
+  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+  return (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
+      (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
+      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
+      (size_t*)sizes, (size_t)sizes_length);
+}
+
 static void
 nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                jint index, jlong value, jint size) {
-  dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID,
-                            (uint32_t)index, (uintptr_t)value, (size_t)size);
+  dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
+                  (uintptr_t)value, (size_t)size);
 }
 
 static void
 nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                   jlong fieldID, jlong value, jint size) {
   dispatchTab.ClosureSetGlobal((RsContext)con, (RsClosure)closureID,
-                               (RsScriptFieldID)fieldID, (uintptr_t)value,
-                               (size_t)size);
+                     (RsScriptFieldID)fieldID, (uintptr_t)value, (size_t)size);
 }
 
 static long
 nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
-                    jlongArray closureArray) {
+                    jstring cacheDir, jlongArray closureArray) {
+  AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
+
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
   jsize numClosures = _env->GetArrayLength(closureArray);
   RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
@@ -415,9 +446,14 @@
     closures[i] = (RsClosure)jClosures[i];
   }
 
-  return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create((RsContext)con,
-                                                          closures,
-                                                          numClosures);
+  return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
+      (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+      closures, numClosures);
+}
+
+static void
+nScriptGroup2Execute(JNIEnv *_env, jobject _this, jlong con, jlong groupID) {
+  dispatchTab.ScriptGroupExecute((RsContext)con, (RsScriptGroup2)groupID);
 }
 
 static void
@@ -1620,9 +1656,10 @@
 {"rsnContextDestroy",                "(J)V",                                  (void*)nContextDestroy },
 {"rsnContextDump",                   "(JI)V",                                 (void*)nContextDump },
 {"rsnContextSendMessage",            "(JI[I)V",                               (void*)nContextSendMessage },
-//{"rsnClosureCreate",                 "(JJJ[J[J[I[J[J)J",                      (void*)nClosureCreate },
-//{"rsnClosureSetArg",                 "(JJIJI)V",                              (void*)nClosureSetArg },
-//{"rsnClosureSetGlobal",              "(JJJJI)V",                              (void*)nClosureSetGlobal },
+{"rsnClosureCreate",                 "(JJJ[J[J[I[J[J)J",                      (void*)nClosureCreate },
+{"rsnInvokeClosureCreate",           "(JJ[B[J[J[I)J",                         (void*)nInvokeClosureCreate },
+{"rsnClosureSetArg",                 "(JJIJI)V",                              (void*)nClosureSetArg },
+{"rsnClosureSetGlobal",              "(JJJJI)V",                              (void*)nClosureSetGlobal },
 {"rsnObjDestroy",                    "(JJ)V",                                 (void*)nObjDestroy },
 
 {"rsnElementCreate",                 "(JJIZI)J",                              (void*)nElementCreate },
@@ -1680,10 +1717,11 @@
 {"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
 {"rsnScriptFieldIDCreate",           "(JJIZ)J",                               (void*)nScriptFieldIDCreate },
 {"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-//{"rsnScriptGroup2Create",            "(J[J)J",                                (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create",            "(JLjava/lang/String;[J)J",              (void*)nScriptGroup2Create },
 {"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
 {"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
 {"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
+{"rsnScriptGroup2Execute",           "(JJ)V",                                 (void*)nScriptGroup2Execute },
 
 {"rsnSamplerCreate",                 "(JIIIIIF)J",                            (void*)nSamplerCreate },