Refactor RuntimeShader to support easier assignment of uniforms.
uniforms are no longer set as single block of bytes where the caller
has to know the offset, but instead by the string identfier in the
shader. This CL only supports floats, but future expansions of this
will provide helpers for ints, colors, and byte[].
Also by storing the shaders and uniforms in the RuntimeShaderBuilder
we can more easily copy them. This enables Canvas.drawRipple to not
just copy the compiled SkSL effect, but also all the uniforms and
other input shaders set on the RuntimeShader.
Bug: 177051137
Test: HwAccelerationTest
Change-Id: I4733f42ba662546be6bebc37f0b89832778e66ce
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 49888fd..6d68d39 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -220,7 +220,7 @@
CanvasProperty<Float> progress, RuntimeShader shader) {
nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
radius.getNativeContainer(), paint.getNativeContainer(),
- progress.getNativeContainer(), shader.getNativeShaderFactory());
+ progress.getNativeContainer(), shader.getNativeShaderBuilder());
}
/**
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 7f2e503..1ace322 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -17,7 +17,6 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import libcore.util.NativeAllocationRegistry;
@@ -33,14 +32,12 @@
RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
}
- private byte[] mUniforms;
- private Shader[] mInputShaders;
private boolean mIsOpaque;
/**
- * Current native shader factory instance.
+ * Current native shader builder instance.
*/
- private long mNativeInstanceRuntimeShaderFactory;
+ private long mNativeInstanceRuntimeShaderBuilder;
/**
* Creates a new RuntimeShader.
@@ -50,80 +47,86 @@
* on number of uniforms declared by sksl.
* @param isOpaque True if all pixels have alpha 1.0f.
*/
- public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) {
- this(sksl, uniforms, null, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
- }
-
- /**
- * 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;
+ public RuntimeShader(@NonNull String sksl, boolean isOpaque) {
+ super(ColorSpace.get(ColorSpace.Named.SRGB));
mIsOpaque = isOpaque;
- mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
- mNativeInstanceRuntimeShaderFactory);
+ mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(
+ this, mNativeInstanceRuntimeShaderBuilder);
}
/**
- * Sets new value for shader parameters.
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than float then an
+ * IllegalArgumentException is thrown.
*
- * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
- * on number of uniforms declared by mSksl.
+ * @param uniformName name matching the uniform declared in the SKSL shader
+ * @param value
*/
- public void updateUniforms(@Nullable byte[] uniforms) {
- mUniforms = uniforms;
+ public void setUniform(@NonNull String uniformName, float value) {
+ setUniform(uniformName, new float[] {value});
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than float2/vec2 then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the SKSL shader
+ * @param value1
+ * @param value2
+ */
+ public void setUniform(@NonNull String uniformName, float value1, float value2) {
+ setUniform(uniformName, new float[] {value1, value2});
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than a vecN/floatN where N is
+ * the size of the values array then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the SKSL shader
+ * @param values
+ */
+ public void setUniform(@NonNull String uniformName, float[] values) {
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values);
discardNativeInstance();
}
/**
- * Sets new values for the shaders that serve as inputs to this shader.
+ * Sets the uniform shader that is declares as input to this shader. If the shader does not
+ * have a uniform shader with that name then an IllegalArgumentException is thrown.
*
- * @param shaderInputs Array of Shaders passed into the SKSL shader. Array size depends
- * on number of input shaders declared by sksl.
+ * @param shaderName name matching the uniform declared in the SKSL shader
+ * @param shader shader passed into the SKSL shader for sampling
*/
- public void updateInputShaders(@Nullable Shader[] shaderInputs) {
- mInputShaders = shaderInputs;
+ public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+ nativeUpdateShader(
+ mNativeInstanceRuntimeShaderBuilder, shaderName, shader.getNativeInstance());
discardNativeInstance();
}
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
- long[] nativeShaders = mInputShaders.length > 0 ? new long[mInputShaders.length] : null;
- for (int i = 0; i < mInputShaders.length; i++) {
- nativeShaders[i] = mInputShaders[i].getNativeInstance(filterFromPaint);
- }
-
- return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
- nativeShaders, colorSpace().getNativeInstance(), mIsOpaque);
+ return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mIsOpaque);
}
- public long getNativeShaderFactory() {
- return mNativeInstanceRuntimeShaderFactory;
+ public long getNativeShaderBuilder() {
+ return mNativeInstanceRuntimeShaderBuilder;
}
- private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
- long[] shaderInputs, long colorSpaceHandle, boolean isOpaque);
-
- private static native long nativeCreateShaderFactory(String sksl);
+ public boolean isOpaque() {
+ return mIsOpaque;
+ }
private static native long nativeGetFinalizer();
+ private static native long nativeCreateBuilder(String sksl);
+ private static native long nativeCreateShader(
+ long shaderBuilder, long matrix, boolean isOpaque);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, float[] uniforms);
+ private static native void nativeUpdateShader(
+ long shaderBuilder, String shaderName, long shader);
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 815ffde..1ebc489 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -819,10 +819,10 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) {
+ const SkRuntimeShaderBuilder& effectBuilder) {
sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable(
new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress,
- runtimeEffect));
+ effectBuilder));
mCanvas->drawDrawable(drawable.get());
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index fa7d373..b53006e 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -152,7 +152,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) override;
+ const SkRuntimeShaderBuilder& effectBuilder) override;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index ea9fea97..fa0c45b 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -161,7 +161,7 @@
}
SkRuntimeShaderBuilder::BuilderUniform radiusU =
- runtimeEffectBuilder.uniform("in_maxRadius");
+ runtimeEffectBuilder.uniform("in_radius");
if (radiusU.fVar != nullptr) {
radiusU = radius->value;
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d0c996b..0b103a7 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -31,7 +31,7 @@
class SkAnimatedImage;
class SkCanvasState;
-class SkRuntimeEffect;
+class SkRuntimeShaderBuilder;
class SkVertices;
namespace minikin {
@@ -139,7 +139,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) = 0;
+ const SkRuntimeShaderBuilder& effectBuilder) = 0;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index aaec60b..ad3fd55 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -1,3 +1,6 @@
+#undef LOG_TAG
+#define LOG_TAG "ShaderJNI"
+
#include "GraphicsJNI.h"
#include "SkColorFilter.h"
#include "SkGradientShader.h"
@@ -232,53 +235,72 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
- 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, shaderVector.data(), shaderVector.size(),
- matrix, isOpaque == JNI_TRUE);
- ThrowIAE_IfNull(env, shader);
-
- return reinterpret_cast<jlong>(shader.release());
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
+static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
ScopedUtfChars strSksl(env, sksl);
auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()));
sk_sp<SkRuntimeEffect> effect = std::get<0>(result);
- if (!effect) {
+ if (effect.get() == nullptr) {
const auto& err = std::get<1>(result);
doThrowIAE(env, err.c_str());
+ return 0;
}
- return reinterpret_cast<jlong>(effect.release());
+ return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(effect)));
}
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-static void Effect_safeUnref(SkRuntimeEffect* effect) {
- SkSafeUnref(effect);
+static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
+ delete builder;
}
static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
+}
+
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
+ jboolean isOpaque) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE);
+ ThrowIAE_IfNull(env, shader);
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+}
+
+static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloatArray jvalues) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
+ } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length());
+ }
+}
+
+static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jlong shaderHandle) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
+
+ SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
+ if (child.fIndex == -1) {
+ ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
+ return;
+ }
+
+ builder->child(name.c_str()) = sk_ref_sp(shader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -313,10 +335,11 @@
};
static const JNINativeMethod gRuntimeShaderMethods[] = {
- { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer },
- { "nativeCreate", "(JJ[B[JJZ)J", (void*)RuntimeShader_create },
- { "nativeCreateShaderFactory", "(Ljava/lang/String;)J",
- (void*)RuntimeShader_createShaderFactory },
+ {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
+ {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
+ {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
+ {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
};
int register_android_graphics_Shader(JNIEnv* env)
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index f4877f4..0bbcbe4 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -142,7 +142,8 @@
static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
jlong xPropPtr, jlong yPropPtr,
jlong radiusPropPtr, jlong paintPropPtr,
- jlong progressPropPtr, jlong effectPtr) {
+ jlong progressPropPtr,
+ jlong builderPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
@@ -150,8 +151,8 @@
CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
CanvasPropertyPrimitive* progressProp =
reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr);
- SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(effectPtr);
- canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, sk_ref_sp(effect));
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr);
+ canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, *builder);
}
static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index 3142d92..7859145 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -61,13 +61,13 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect)
+ const SkRuntimeShaderBuilder& effectBuilder)
: mX(x)
, mY(y)
, mRadius(radius)
, mPaint(paint)
, mProgress(progress)
- , mRuntimeEffectBuilder(std::move(runtimeEffect)) {}
+ , mRuntimeEffectBuilder(effectBuilder) {}
protected:
virtual SkRect onGetBounds() override {
@@ -83,7 +83,7 @@
}
SkRuntimeShaderBuilder::BuilderUniform radiusU =
- mRuntimeEffectBuilder.uniform("in_maxRadius");
+ mRuntimeEffectBuilder.uniform("in_radius");
if (radiusU.fVar != nullptr) {
radiusU = mRadius->value;
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 7faebda..a8870e5 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -90,9 +90,9 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) {
+ const SkRuntimeShaderBuilder& effectBuilder) {
drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress,
- runtimeEffect));
+ effectBuilder));
}
void SkiaRecordingCanvas::enableZ(bool enableZ) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 622df43..fd44d33 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -71,7 +71,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) override;
+ const SkRuntimeShaderBuilder& effectBuilder) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 9a2def9..1a940c7 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1016,6 +1016,15 @@
</intent-filter>
</activity>
+ <activity android:name="RippleActivity"
+ android:label="Animation/Ripple Animation"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="MultiProducerActivity"
android:label="Threads/Multiple Producers"
android:exported="true">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index b53b78a..8be3b7e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -35,9 +35,6 @@
import android.os.Bundle;
import android.view.View;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
@SuppressWarnings({"UnusedDeclaration"})
public class ColorFiltersMutateActivity extends Activity {
@Override
@@ -54,11 +51,13 @@
private final Paint mLightingPaint;
private final Paint mBlendPaint;
private final Paint mShaderPaint;
+ private final RuntimeShader mRuntimeShader;
private float mSaturation = 0.0f;
private int mLightAdd = 0;
private int mLightMul = 0;
private int mPorterDuffColor = 0;
+ private float mShaderParam1 = 0.0f;
static final String sSkSL =
"in shader bitmapShader;\n"
@@ -67,8 +66,6 @@
+ " return half4(sample(bitmapShader, xy).rgb, param1);\n"
+ "}\n";
- private byte[] mUniforms = new byte[4];
-
BitmapsView(Context c) {
super(c);
@@ -86,11 +83,13 @@
mBlendPaint = new Paint();
mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
+ mRuntimeShader = new RuntimeShader(sSkSL, false);
+ mRuntimeShader.setUniform("param1", mShaderParam1);
+ mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1,
+ Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP));
mShaderPaint = new Paint();
- Shader[] inputShaders = { new BitmapShader(mBitmap1, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP) };
- mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, inputShaders, true));
- setShaderParam1(0.0f);
+ mShaderPaint.setShader(mRuntimeShader);
ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f);
sat.setDuration(1000);
@@ -177,20 +176,15 @@
}
public void setShaderParam1(float value) {
- RuntimeShader shader = (RuntimeShader) mShaderPaint.getShader();
- ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.putFloat(value);
- shader.updateUniforms(mUniforms);
+ mShaderParam1 = value;
+ mRuntimeShader.setUniform("param1", mShaderParam1);
invalidate();
}
// If either valueFrom or valueTo is null, then a getter function will also be derived
// and called by the animator class.
public float getShaderParam1() {
- ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- return buffer.getFloat();
+ return mShaderParam1;
}
@Override
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
new file mode 100644
index 0000000..f6d9a73
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 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 com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RuntimeShader;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import java.util.ArrayList;
+
+public class RippleActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(new RippleView(this),
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+ setContentView(layout);
+ }
+
+ static class RippleView extends View {
+ static final int DURATION = 1000;
+ static final int MAX_RADIUS = 250;
+
+ private boolean mToggle = false;
+ ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>();
+
+ CanvasProperty<Float> mX;
+ CanvasProperty<Float> mY;
+ CanvasProperty<Float> mRadius;
+ CanvasProperty<Float> mProgress;
+ CanvasProperty<Paint> mPaint;
+ RuntimeShader mRuntimeShader;
+
+ static final String sSkSL = ""
+ + "uniform float2 in_origin;"
+ + "uniform float in_progress;\n"
+ + "uniform float in_maxRadius;\n"
+ + "uniform shader in_paintColor;\n"
+ + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + "
+ + "(pf.y - p0.y) * (pf.y - p0.y)); }\n"
+ + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n"
+ + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233)))"
+ + " * 43758.5453123); }\n"
+ + "float4 main(float2 p)\n"
+ + "{\n"
+ + " float fraction = in_progress;\n"
+ + " float2 fragCoord = p;//sk_FragCoord.xy;\n"
+ + " float maxDist = in_maxRadius;\n"
+ + " float fragDist = dist2(in_origin, fragCoord.xy);\n"
+ + " float circleRadius = maxDist * fraction;\n"
+ + " float colorVal = (fragDist - circleRadius) / maxDist;\n"
+ + " float d = fragDist < circleRadius \n"
+ + " ? 1. - abs(colorVal * 2. * smoothstep(0., 1., fraction)) \n"
+ + " : 1. - abs(colorVal * 3.);\n"
+ + " d = smoothstep(0., 1., d);\n"
+ + " float divider = 2.;\n"
+ + " float x = floor(fragCoord.x / divider);\n"
+ + " float y = floor(fragCoord.y / divider);\n"
+ + " float density = .95;\n"
+ + " d = rand(float2(x, y)) > density ? d : d * .2;\n"
+ + " d = d * rand(float2(fraction, x * y));\n"
+ + " float alpha = 1. - pow(fraction, 3.);\n"
+ + " return float4(sample(in_paintColor).rgb, d * alpha);\n"
+ + "}";
+
+ RippleView(Context c) {
+ super(c);
+ setClickable(true);
+
+ mX = CanvasProperty.createFloat(200.0f);
+ mY = CanvasProperty.createFloat(200.0f);
+ mRadius = CanvasProperty.createFloat(150.0f);
+ mProgress = CanvasProperty.createFloat(0.0f);
+
+ Paint p = new Paint();
+ p.setAntiAlias(true);
+ p.setColor(0xFFFF0000);
+ mPaint = CanvasProperty.createPaint(p);
+
+ mRuntimeShader = new RuntimeShader(sSkSL, false);
+ mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (canvas.isHardwareAccelerated()) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mRuntimeShader);
+ }
+ }
+
+ @Override
+ public boolean performClick() {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ mRunningAnimations.get(i).cancel();
+ }
+ mRunningAnimations.clear();
+
+ mToggle = !mToggle;
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mX, mToggle ? 400.0f : 200.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mY, mToggle ? 600.0f : 200.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mRadius, mToggle ? MAX_RADIUS : 150.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mProgress, mToggle ? 1.0f : 0.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
+
+ // Will be "chained" to run after the above
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f));
+
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RenderNodeAnimator anim = mRunningAnimations.get(i);
+ anim.setDuration(DURATION);
+ anim.setTarget(this);
+ if (i == (mRunningAnimations.size() - 1)) {
+ // "chain" test
+ anim.setStartValue(64.0f);
+ anim.setStartDelay(anim.getDuration());
+ }
+ anim.start();
+ }
+
+ if (mToggle) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy");
+ try {
+ Thread.sleep(DURATION);
+ } catch (InterruptedException e) {
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ });
+ }
+ return true;
+ }
+ }
+}