diff options
| -rw-r--r-- | core/api/current.txt | 20 | ||||
| -rw-r--r-- | graphics/java/android/graphics/RuntimeShader.java | 292 | ||||
| -rw-r--r-- | libs/hwui/jni/Shader.cpp | 99 |
3 files changed, 371 insertions, 40 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 623bff39a9bc..49689e45275b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -16589,6 +16589,26 @@ package android.graphics { method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint); } + public class RuntimeShader extends android.graphics.Shader { + ctor public RuntimeShader(@NonNull String); + ctor public RuntimeShader(@NonNull String, boolean); + method public boolean isForceOpaque(); + method public void setColorUniform(@NonNull String, @ColorInt int); + method public void setColorUniform(@NonNull String, @ColorLong long); + method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color); + method public void setFloatUniform(@NonNull String, float); + method public void setFloatUniform(@NonNull String, float, float); + method public void setFloatUniform(@NonNull String, float, float, float); + method public void setFloatUniform(@NonNull String, float, float, float, float); + method public void setFloatUniform(@NonNull String, @NonNull float[]); + method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader); + method public void setIntUniform(@NonNull String, int); + method public void setIntUniform(@NonNull String, int, int); + method public void setIntUniform(@NonNull String, int, int, int); + method public void setIntUniform(@NonNull String, int, int, int, int); + method public void setIntUniform(@NonNull String, @NonNull int[]); + } + public class Shader { ctor @Deprecated public Shader(); method public boolean getLocalMatrix(@NonNull android.graphics.Matrix); diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 1ace32277f0a..9ca8e3b46317 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -16,13 +16,15 @@ package android.graphics; +import android.annotation.ColorInt; +import android.annotation.ColorLong; import android.annotation.NonNull; import libcore.util.NativeAllocationRegistry; /** - * Shader that calculates pixel output with a program (fragment shader) running on a GPU. - * @hide + * Shader that calculates per-pixel color via a user defined Android Graphics Shading Language + * (AGSL) function. */ public class RuntimeShader extends Shader { @@ -32,7 +34,7 @@ public class RuntimeShader extends Shader { RuntimeShader.class.getClassLoader(), nativeGetFinalizer()); } - private boolean mIsOpaque; + private boolean mForceOpaque; /** * Current native shader builder instance. @@ -42,57 +44,270 @@ public class RuntimeShader extends Shader { /** * 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 isOpaque True if all pixels have alpha 1.0f. + * @param shader The text of AGSL shader program to run. */ - public RuntimeShader(@NonNull String sksl, boolean isOpaque) { + public RuntimeShader(@NonNull String shader) { + this(shader, false); + } + + /** + * Creates a new RuntimeShader. + * + * @param shader The text of AGSL shader program to run. + * @param forceOpaque If true then all pixels produced by the AGSL shader program will have an + * alpha of 1.0f. + */ + public RuntimeShader(@NonNull String shader, boolean forceOpaque) { + // colorspace is required, but the RuntimeShader always produces colors in the destination + // buffer's colorspace regardless of the value specified here. super(ColorSpace.get(ColorSpace.Named.SRGB)); - mIsOpaque = isOpaque; - mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl); + if (shader == null) { + throw new NullPointerException("RuntimeShader requires a non-null AGSL string"); + } + mForceOpaque = forceOpaque; + mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(shader); NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeInstanceRuntimeShaderBuilder); } + public boolean isForceOpaque() { + return mForceOpaque; + } + + /** + * Sets the uniform color 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 vec3 or vec4 and + * corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL shader program + * @param color the provided sRGB color will be transformed into the shader program's output + * colorspace and will be available as a vec4 uniform in the program. + */ + public void setColorUniform(@NonNull String uniformName, @ColorInt int color) { + setUniform(uniformName, Color.valueOf(color).getComponents(), true); + } + + /** + * Sets the uniform color 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 vec3 or vec4 and + * corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL shader program + * @param color the provided sRGB color will be transformed into the shader program's output + * colorspace and will be available as a vec4 uniform in the program. + */ + public void setColorUniform(@NonNull String uniformName, @ColorLong long color) { + Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); + setUniform(uniformName, exSRGB.getComponents(), true); + } + + /** + * Sets the uniform color 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 vec3 or vec4 and + * corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL shader program + * @param color the provided sRGB color will be transformed into the shader program's output + * colorspace and will be available as a vec4 uniform in the program. + */ + public void setColorUniform(@NonNull String uniformName, @NonNull Color color) { + if (color == null) { + throw new NullPointerException("The color parameter must not be null"); + } + Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); + setUniform(uniformName, exSRGB.getComponents(), true); + } + + /** + * 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 float or float[1] + * then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setFloatUniform(@NonNull String uniformName, float value) { + setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1); + } + /** * 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 + * with that name or if the uniform is declared with a type other than vec2 or float[2] then an * IllegalArgumentException is thrown. * - * @param uniformName name matching the uniform declared in the SKSL shader - * @param value + * @param uniformName name matching the uniform declared in the AGSL shader program */ - public void setUniform(@NonNull String uniformName, float value) { - setUniform(uniformName, new float[] {value}); + public void setFloatUniform(@NonNull String uniformName, float value1, float value2) { + setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2); + } /** * 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 + * with that name or if the uniform is declared with a type other than vec3 or float[3] then an * IllegalArgumentException is thrown. * - * @param uniformName name matching the uniform declared in the SKSL shader - * @param value1 - * @param value2 + * @param uniformName name matching the uniform declared in the AGSL shader program */ - public void setUniform(@NonNull String uniformName, float value1, float value2) { - setUniform(uniformName, new float[] {value1, value2}); + public void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3) { + setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3); + + } + + /** + * 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 vec4 or float[4] then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3, float value4) { + setFloatUniform(uniformName, value1, value2, value3, value4, 4); } /** * 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. + * with that name or if the uniform is declared with a type other than a float (for N=1), vecN, + * or float[N] where N is the length of the values param then an IllegalArgumentException is + * thrown. * - * @param uniformName name matching the uniform declared in the SKSL shader - * @param values + * @param uniformName name matching the uniform declared in the AGSL shader program */ + public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) { + setUniform(uniformName, values, false); + } + + /** + * Old method signature used by some callers within the platform code + * @hide + * @deprecated use setFloatUniform instead + */ + @Deprecated public void setUniform(@NonNull String uniformName, float[] values) { + setFloatUniform(uniformName, values); + } + + /** + * Old method signature used by some callers within the platform code + * @hide + * @deprecated use setFloatUniform instead + */ + @Deprecated + public void setUniform(@NonNull String uniformName, float value) { + setFloatUniform(uniformName, value); + } + + /** + * Old method signature used by some callers within the platform code + * @hide + * @deprecated use setFloatUniform instead + */ + @Deprecated + public void setUniform(@NonNull String uniformName, float value1, float value2) { + setFloatUniform(uniformName, value1, value2); + } + + private void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3, float value4, int count) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + + nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2, + value3, value4, count); + discardNativeInstance(); + } + + private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + if (values == null) { + throw new NullPointerException("The uniform values parameter must not be null"); + } + + nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values, isColor); + discardNativeInstance(); + } + + /** + * 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 an int or int[1] + * then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setIntUniform(@NonNull String uniformName, int value) { + setIntUniform(uniformName, value, 0, 0, 0, 1); + } + + /** + * 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 ivec2 or int[2] then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2) { + setIntUniform(uniformName, value1, value2, 0, 0, 2); + + } + + /** + * 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 ivec3 or int[3] then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) { + setIntUniform(uniformName, value1, value2, value3, 0, 3); + + } + + /** + * 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 ivec4 or int[4] then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2, + int value3, int value4) { + setIntUniform(uniformName, value1, value2, value3, value4, 4); + } + + /** + * 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 an int (for N=1), ivecN, + * or int[N] where N is the length of the values param then an IllegalArgumentException is + * thrown. + * + * @param uniformName name matching the uniform declared in the AGSL shader program + */ + public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + if (values == null) { + throw new NullPointerException("The uniform values parameter must not be null"); + } nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values); discardNativeInstance(); } + private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3, + int value4, int count) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + + nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2, + value3, value4, count); + discardNativeInstance(); + } + /** * 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. @@ -101,6 +316,12 @@ public class RuntimeShader extends Shader { * @param shader shader passed into the SKSL shader for sampling */ public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) { + if (shaderName == null) { + throw new NullPointerException("The shaderName parameter must not be null"); + } + if (shader == null) { + throw new NullPointerException("The shader parameter must not be null"); + } nativeUpdateShader( mNativeInstanceRuntimeShaderBuilder, shaderName, shader.getNativeInstance()); discardNativeInstance(); @@ -109,23 +330,28 @@ public class RuntimeShader extends Shader { /** @hide */ @Override protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { - return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mIsOpaque); + return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mForceOpaque); } - public long getNativeShaderBuilder() { + /** @hide */ + protected long getNativeShaderBuilder() { return mNativeInstanceRuntimeShaderBuilder; } - public boolean isOpaque() { - return mIsOpaque; - } - private static native long nativeGetFinalizer(); - private static native long nativeCreateBuilder(String sksl); + private static native long nativeCreateBuilder(String agsl); private static native long nativeCreateShader( long shaderBuilder, long matrix, boolean isOpaque); private static native void nativeUpdateUniforms( - long shaderBuilder, String uniformName, float[] uniforms); + long shaderBuilder, String uniformName, float[] uniforms, boolean isColor); + private static native void nativeUpdateUniforms( + long shaderBuilder, String uniformName, float value1, float value2, float value3, + float value4, int count); + private static native void nativeUpdateUniforms( + long shaderBuilder, String uniformName, int[] uniforms); + private static native void nativeUpdateUniforms( + long shaderBuilder, String uniformName, int value1, int value2, int value3, + int value4, int count); private static native void nativeUpdateShader( long shaderBuilder, String shaderName, long shader); } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 1b3892032e79..c4366f756a21 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -273,21 +273,99 @@ static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { return ret; } -static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder, - jstring jUniformName, jfloatArray jvalues) { +static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { + switch (type) { + case SkRuntimeEffect::Uniform::Type::kFloat: + case SkRuntimeEffect::Uniform::Type::kFloat2: + case SkRuntimeEffect::Uniform::Type::kFloat3: + case SkRuntimeEffect::Uniform::Type::kFloat4: + case SkRuntimeEffect::Uniform::Type::kFloat2x2: + case SkRuntimeEffect::Uniform::Type::kFloat3x3: + case SkRuntimeEffect::Uniform::Type::kFloat4x4: + return false; + case SkRuntimeEffect::Uniform::Type::kInt: + case SkRuntimeEffect::Uniform::Type::kInt2: + case SkRuntimeEffect::Uniform::Type::kInt3: + case SkRuntimeEffect::Uniform::Type::kInt4: + return true; + } +} + +static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, + const char* uniformName, const float values[], int count, + bool isColor) { + SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { + if (isColor) { + jniThrowExceptionFmt( + env, "java/lang/IllegalArgumentException", + "attempting to set a color uniform using the non-color specific APIs: %s %x", + uniformName, uniform.fVar->flags); + } else { + ThrowIAEFmt(env, + "attempting to set a non-color uniform using the setColorUniform APIs: %s", + uniformName); + } + } else if (isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", + uniformName); + } else if (!uniform.set<float>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jfloat value1, jfloat value2, + jfloat value3, jfloat value4, jint count) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + const float values[4] = {value1, value2, value3, value4}; + UpdateFloatUniforms(env, builder, name.c_str(), values, count, false); +} + +static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jfloatArray jvalues, + jboolean isColor) { SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); ScopedUtfChars name(env, jUniformName); AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); + UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor); +} - SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str()); +static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName, + const int values[], int count) { + SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName); 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, "unable to find uniform named %s", uniformName); + } else if (!isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", + uniformName); + } else if (!uniform.set<int>(values, count)) { ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", - uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length()); + uniform.fVar->sizeInBytes(), sizeof(float) * count); } } +static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jint value1, jint value2, + jint value3, jint value4, jint count) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + const int values[4] = {value1, value2, value3, value4}; + UpdateIntUniforms(env, builder, name.c_str(), values, count); +} + +static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jintArray jvalues) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + AutoJavaIntArray autoValues(env, jvalues, 0); + UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length()); +} + static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder, jstring jUniformName, jlong shaderHandle) { SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); @@ -338,7 +416,14 @@ static const JNINativeMethod gRuntimeShaderMethods[] = { {"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}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", + (void*)RuntimeShader_updateFloatArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", + (void*)RuntimeShader_updateFloatUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", + (void*)RuntimeShader_updateIntArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", + (void*)RuntimeShader_updateIntUniforms}, {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader}, }; |