Add basic support for input shaders to RuntimeShader.

This adds the ability for Java RuntimeShaders to provide other
shaders as inputs.  This is a first pass API and should be updated
to mirror the SkRuntimeShaderBuilder API that enable callers to set
the uniforms and inputs shaders by name instead of passing arrays.

Test: HwAccelerationTests
Bug: n/a
Change-Id: I53a15ad864bede2fecc1e2459dca983d224114a0
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 5a0b4a9..63089e2 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -34,6 +34,7 @@
     }
 
     private byte[] mUniforms;
+    private Shader[] mInputShaders;
     private boolean mIsOpaque;
 
     /**
@@ -50,13 +51,30 @@
      * @param isOpaque True if all pixels have alpha 1.0f.
      */
     public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) {
-        this(sksl, uniforms, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+        this(sksl, uniforms, null, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
     }
 
-    private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque,
-            ColorSpace colorSpace) {
+    /**
+     * Creates a new RuntimeShader.
+     *
+     * @param sksl The text of SKSL program to run on the GPU.
+     * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+     *                 on number of uniforms declared by sksl.
+     * @param shaderInputs Array of shaders passed to the SKSL shader. Array size depends
+     *                     on the number of input shaders declared in the sksl
+     * @param isOpaque True if all pixels have alpha 1.0f.
+     */
+    public  RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
+                          @Nullable Shader[] shaderInputs, boolean isOpaque) {
+        this(sksl, uniforms, shaderInputs, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
+                          @Nullable Shader[] shaderInputs, boolean isOpaque,
+                          ColorSpace colorSpace) {
         super(colorSpace);
         mUniforms = uniforms;
+        mInputShaders = shaderInputs;
         mIsOpaque = isOpaque;
         mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
@@ -74,15 +92,31 @@
         discardNativeInstance();
     }
 
+    /**
+     * Sets new values for the shaders that serve as inputs to this shader.
+     *
+     * @param shaderInputs Array of Shaders passed into the SKSL shader. Array size depends
+     *                     on number of input shaders declared by sksl.
+     */
+    public void updateInputShaders(@Nullable Shader[] shaderInputs) {
+        mInputShaders = shaderInputs;
+        discardNativeInstance();
+    }
+
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix) {
+        long[] nativeShaders = mInputShaders.length > 0 ? new long[mInputShaders.length] : null;
+        for (int i = 0; i < mInputShaders.length; i++) {
+            nativeShaders[i] = mInputShaders[i].getNativeInstance();
+        }
+
         return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
-                colorSpace().getNativeInstance(), mIsOpaque);
+                nativeShaders, colorSpace().getNativeInstance(), mIsOpaque);
     }
 
     private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
-            long colorSpaceHandle, boolean isOpaque);
+            long[] shaderInputs, long colorSpaceHandle, boolean isOpaque);
 
     private static native long nativeCreateShaderFactory(String sksl);
 
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index e76aace..e36e355 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -211,14 +211,26 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
-        jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+        jbyteArray inputs, jlongArray inputShaders, jlong colorSpaceHandle, jboolean isOpaque) {
     SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
     AutoJavaByteArray arInputs(env, inputs);
 
+    std::vector<sk_sp<SkShader>> shaderVector;
+    if (inputShaders) {
+        jsize shaderCount = env->GetArrayLength(inputShaders);
+        shaderVector.resize(shaderCount);
+        jlong* arrayPtr = env->GetLongArrayElements(inputShaders, NULL);
+        for (int i = 0; i < shaderCount; i++) {
+            shaderVector[i] = sk_ref_sp(reinterpret_cast<SkShader*>(arrayPtr[i]));
+        }
+        env->ReleaseLongArrayElements(inputShaders, arrayPtr, 0);
+    }
+
     sk_sp<SkData> fData;
     fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
+    sk_sp<SkShader> shader = effect->makeShader(fData, shaderVector.data(), shaderVector.size(),
+                                                matrix, isOpaque == JNI_TRUE);
     ThrowIAE_IfNull(env, shader);
 
     return reinterpret_cast<jlong>(shader.release());
@@ -280,7 +292,7 @@
 
 static const JNINativeMethod gRuntimeShaderMethods[] = {
     { "nativeGetFinalizer",   "()J",    (void*)RuntimeShader_getNativeFinalizer },
-    { "nativeCreate",     "(JJ[BJZ)J",  (void*)RuntimeShader_create     },
+    { "nativeCreate",     "(JJ[B[JJZ)J",  (void*)RuntimeShader_create     },
     { "nativeCreateShaderFactory",     "(Ljava/lang/String;)J",
       (void*)RuntimeShader_createShaderFactory     },
 };
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 08144c8..b53b78a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
@@ -30,6 +31,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.RuntimeShader;
+import android.graphics.Shader;
 import android.os.Bundle;
 import android.view.View;
 
@@ -59,9 +61,10 @@
         private int mPorterDuffColor = 0;
 
         static final String sSkSL =
-                "uniform float param1;\n"
-                + "void main(float2 xy, inout half4 color) {\n"
-                + "color = half4(color.r, half(param1), color.b, 1.0);\n"
+                "in shader bitmapShader;\n"
+                + "uniform float param1;\n"
+                + "half4 main(float2 xy) {\n"
+                + "  return half4(sample(bitmapShader, xy).rgb, param1);\n"
                 + "}\n";
 
         private byte[] mUniforms = new byte[4];
@@ -84,7 +87,9 @@
             mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
 
             mShaderPaint = new Paint();
-            mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, true));
+            Shader[] inputShaders = { new BitmapShader(mBitmap1, Shader.TileMode.CLAMP,
+                                                       Shader.TileMode.CLAMP) };
+            mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, inputShaders, true));
             setShaderParam1(0.0f);
 
             ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f);