2019-12-19 20:44:56 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
#include "include/core/SkBitmap.h"
|
2021-06-17 02:37:15 +00:00
|
|
|
#include "include/core/SkBlender.h"
|
2020-01-08 18:19:58 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
2020-08-05 20:48:58 +00:00
|
|
|
#include "include/core/SkColorFilter.h"
|
2020-08-06 16:15:53 +00:00
|
|
|
#include "include/core/SkData.h"
|
2020-01-08 18:19:58 +00:00
|
|
|
#include "include/core/SkPaint.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
2021-07-30 15:20:19 +00:00
|
|
|
#include "include/effects/SkBlenders.h"
|
2020-01-02 16:55:24 +00:00
|
|
|
#include "include/effects/SkRuntimeEffect.h"
|
2020-07-06 14:56:46 +00:00
|
|
|
#include "include/gpu/GrDirectContext.h"
|
2021-03-30 20:14:37 +00:00
|
|
|
#include "src/core/SkColorSpacePriv.h"
|
2021-04-28 13:41:06 +00:00
|
|
|
#include "src/core/SkRuntimeEffectPriv.h"
|
2020-04-15 18:18:13 +00:00
|
|
|
#include "src/core/SkTLazy.h"
|
2021-07-06 16:21:37 +00:00
|
|
|
#include "src/gpu/GrCaps.h"
|
2020-07-22 14:19:02 +00:00
|
|
|
#include "src/gpu/GrColor.h"
|
2021-06-10 18:27:53 +00:00
|
|
|
#include "src/gpu/GrDirectContextPriv.h"
|
2021-04-27 13:10:10 +00:00
|
|
|
#include "src/gpu/GrFragmentProcessor.h"
|
2021-06-04 20:52:21 +00:00
|
|
|
#include "src/gpu/effects/GrSkSLFP.h"
|
2019-12-19 20:44:56 +00:00
|
|
|
#include "tests/Test.h"
|
|
|
|
|
2020-01-08 18:19:58 +00:00
|
|
|
#include <algorithm>
|
2020-09-30 17:26:43 +00:00
|
|
|
#include <thread>
|
2020-01-08 18:19:58 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
void test_invalid_effect(skiatest::Reporter* r, const char* src, const char* expected) {
|
2021-04-21 19:57:27 +00:00
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src));
|
2021-01-12 23:39:02 +00:00
|
|
|
REPORTER_ASSERT(r, !effect);
|
|
|
|
REPORTER_ASSERT(r, errorText.contains(expected),
|
|
|
|
"Expected error message to contain \"%s\". Actual message: \"%s\"",
|
|
|
|
expected, errorText.c_str());
|
|
|
|
};
|
2019-12-19 20:44:56 +00:00
|
|
|
|
2021-04-21 19:57:27 +00:00
|
|
|
#define EMPTY_MAIN "half4 main(float2 p) { return half4(0); }"
|
2020-11-10 21:36:01 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_LimitedUniformTypes, r) {
|
2021-04-01 13:56:07 +00:00
|
|
|
// Runtime SkSL supports a limited set of uniform types. No bool, for example:
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "uniform bool b;" EMPTY_MAIN, "uniform");
|
|
|
|
}
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_NoInVariables, r) {
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
// 'in' variables aren't allowed at all:
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "in bool b;" EMPTY_MAIN, "'in'");
|
|
|
|
test_invalid_effect(r, "in float f;" EMPTY_MAIN, "'in'");
|
|
|
|
test_invalid_effect(r, "in float2 v;" EMPTY_MAIN, "'in'");
|
|
|
|
test_invalid_effect(r, "in half3x3 m;" EMPTY_MAIN, "'in'");
|
|
|
|
}
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_UndefinedFunction, r) {
|
2021-04-21 19:57:27 +00:00
|
|
|
test_invalid_effect(r, "half4 missing(); half4 main(float2 p) { return missing(); }",
|
2021-07-21 19:19:34 +00:00
|
|
|
"function 'half4 missing()' is not defined");
|
2021-01-12 23:39:02 +00:00
|
|
|
}
|
2020-07-20 19:18:33 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_UndefinedMain, r) {
|
2020-07-20 19:18:33 +00:00
|
|
|
// Shouldn't be possible to create an SkRuntimeEffect without "main"
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "", "main");
|
|
|
|
}
|
2020-07-21 13:39:27 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_SkCapsDisallowed, r) {
|
2020-11-06 16:45:36 +00:00
|
|
|
// sk_Caps is an internal system. It should not be visible to runtime effects
|
2021-04-21 19:57:27 +00:00
|
|
|
test_invalid_effect(
|
|
|
|
r,
|
|
|
|
"half4 main(float2 p) { return sk_Caps.integerSupport ? half4(1) : half4(0); }",
|
|
|
|
"unknown identifier 'sk_Caps'");
|
2021-01-12 23:39:02 +00:00
|
|
|
}
|
2020-11-06 16:45:36 +00:00
|
|
|
|
2021-09-03 13:48:26 +00:00
|
|
|
DEF_TEST(SkRuntimeEffect_DeadCodeEliminationStackOverflow, r) {
|
|
|
|
// Verify that a deeply-nested loop does not cause stack overflow during SkVM dead-code
|
|
|
|
// elimination.
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(R"(
|
|
|
|
half4 main(half4 color) {
|
|
|
|
half value = color.r;
|
|
|
|
|
|
|
|
for (int a=0; a<10; ++a) { // 10
|
|
|
|
for (int b=0; b<10; ++b) { // 100
|
|
|
|
for (int c=0; c<10; ++c) { // 1000
|
|
|
|
for (int d=0; d<10; ++d) { // 10000
|
|
|
|
++value;
|
|
|
|
}}}}
|
|
|
|
|
|
|
|
return value.xxxx;
|
|
|
|
}
|
|
|
|
)"));
|
|
|
|
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
|
|
|
|
}
|
|
|
|
|
2021-04-28 19:14:09 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectCanDisableES2Restrictions, r) {
|
|
|
|
auto test_valid_es3 = [](skiatest::Reporter* r, const char* sksl) {
|
2021-07-02 14:17:45 +00:00
|
|
|
SkRuntimeEffect::Options opt = SkRuntimeEffectPriv::ES3Options();
|
2021-04-28 19:14:09 +00:00
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl), opt);
|
|
|
|
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
|
|
|
|
};
|
|
|
|
|
|
|
|
test_invalid_effect(r, "float f[2] = float[2](0, 1);" EMPTY_MAIN, "construction of array type");
|
|
|
|
test_valid_es3 (r, "float f[2] = float[2](0, 1);" EMPTY_MAIN);
|
|
|
|
}
|
|
|
|
|
2021-04-20 16:00:10 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectForColorFilter, r) {
|
|
|
|
// Tests that the color filter factory rejects or accepts certain SkSL constructs
|
|
|
|
auto test_valid = [r](const char* sksl) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(sksl));
|
2021-04-28 19:14:09 +00:00
|
|
|
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
|
2021-04-20 16:00:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
auto test_invalid = [r](const char* sksl, const char* expected) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(sksl));
|
|
|
|
REPORTER_ASSERT(r, !effect);
|
|
|
|
REPORTER_ASSERT(r,
|
|
|
|
errorText.contains(expected),
|
|
|
|
"Expected error message to contain \"%s\". Actual message: \"%s\"",
|
|
|
|
expected,
|
|
|
|
errorText.c_str());
|
|
|
|
};
|
|
|
|
|
|
|
|
// Color filters must use the 'half4 main(half4)' signature. Either color can be float4/vec4
|
|
|
|
test_valid("half4 main(half4 c) { return c; }");
|
|
|
|
test_valid("float4 main(half4 c) { return c; }");
|
|
|
|
test_valid("half4 main(float4 c) { return c; }");
|
|
|
|
test_valid("float4 main(float4 c) { return c; }");
|
|
|
|
test_valid("vec4 main(half4 c) { return c; }");
|
|
|
|
test_valid("half4 main(vec4 c) { return c; }");
|
|
|
|
test_valid("vec4 main(vec4 c) { return c; }");
|
|
|
|
|
|
|
|
// Invalid return types
|
|
|
|
test_invalid("void main(half4 c) {}", "'main' must return");
|
|
|
|
test_invalid("half3 main(half4 c) { return c.rgb; }", "'main' must return");
|
|
|
|
|
|
|
|
// Invalid argument types (some are valid as shaders, but not color filters)
|
|
|
|
test_invalid("half4 main() { return half4(1); }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(float2 p) { return half4(1); }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter");
|
|
|
|
|
|
|
|
// sk_FragCoord should not be available
|
|
|
|
test_invalid("half4 main(half4 c) { return sk_FragCoord.xy01; }", "unknown identifier");
|
|
|
|
|
|
|
|
// Sampling a child shader requires that we pass explicit coords
|
|
|
|
test_valid("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 c) { return child.eval(c.rg); }");
|
2021-08-24 14:11:56 +00:00
|
|
|
|
|
|
|
// Sampling a colorFilter requires a color
|
Reland "Better first-class shader & color filter support in runtime effects"
This is a reland of adadb95a9f1ef21ccc5264c7d0bdc83b56cf91e9
... adds a temporary workaround for some Android framework code.
Original change's description:
> Better first-class shader & color filter support in runtime effects
>
> This does a few things, because they're all intertwined:
>
> 1) SkRuntimeEffect's API now includes details about children (which Skia
> stage was declared, not just the name). The factories verify that the
> declared types in the SkSL match up with the C++ types being passed.
> Today, we still only support adding children of the same type, so the
> checks are simple. Once we allow mixing types, we'll be testing the
> declared type against the actual C++ type supplied for each slot.
> 2) Adds sample variants that supply the input color to the child. This
> is now the only way to invoke a colorFilter child. Internally, we
> support passing a color when invoking a child shader, but I'm not
> exposing that. It's not clearly part of the semantics of the Skia
> pipeline, and is almost never useful. It also exposes users to
> several inconsistencies (skbug.com/11942).
> 3) Because of #2, it's possible that we can't compute a reusable program
> to filter individual colors. In that case, we don't set the constant
> output for constant input optimization, and filterColor4f falls back
> to the slower base-class implementation.
>
> Bug: skia:11813 skia:11942
> Change-Id: I06c41e1b35056e486f3163a72acf6b9535d7fed4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/401917
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Klein <mtklein@google.com>
Bug: skia:11813 skia:11942
Change-Id: I2c31b147ed86fa8c4dddefb7066bc1d07fe0d285
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404637
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-04-21 13:57:19 +00:00
|
|
|
test_valid("uniform colorFilter child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 c) { return child.eval(c); }");
|
Reland "Better first-class shader & color filter support in runtime effects"
This is a reland of adadb95a9f1ef21ccc5264c7d0bdc83b56cf91e9
... adds a temporary workaround for some Android framework code.
Original change's description:
> Better first-class shader & color filter support in runtime effects
>
> This does a few things, because they're all intertwined:
>
> 1) SkRuntimeEffect's API now includes details about children (which Skia
> stage was declared, not just the name). The factories verify that the
> declared types in the SkSL match up with the C++ types being passed.
> Today, we still only support adding children of the same type, so the
> checks are simple. Once we allow mixing types, we'll be testing the
> declared type against the actual C++ type supplied for each slot.
> 2) Adds sample variants that supply the input color to the child. This
> is now the only way to invoke a colorFilter child. Internally, we
> support passing a color when invoking a child shader, but I'm not
> exposing that. It's not clearly part of the semantics of the Skia
> pipeline, and is almost never useful. It also exposes users to
> several inconsistencies (skbug.com/11942).
> 3) Because of #2, it's possible that we can't compute a reusable program
> to filter individual colors. In that case, we don't set the constant
> output for constant input optimization, and filterColor4f falls back
> to the slower base-class implementation.
>
> Bug: skia:11813 skia:11942
> Change-Id: I06c41e1b35056e486f3163a72acf6b9535d7fed4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/401917
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Klein <mtklein@google.com>
Bug: skia:11813 skia:11942
Change-Id: I2c31b147ed86fa8c4dddefb7066bc1d07fe0d285
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404637
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-04-21 13:57:19 +00:00
|
|
|
|
2021-08-24 14:11:56 +00:00
|
|
|
// Sampling a blender requires two colors
|
|
|
|
test_valid("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 c) { return child.eval(c, c); }");
|
2021-04-20 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 15:34:37 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectForBlender, r) {
|
|
|
|
// Tests that the blender factory rejects or accepts certain SkSL constructs
|
|
|
|
auto test_valid = [r](const char* sksl) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(sksl));
|
|
|
|
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
|
|
|
|
};
|
|
|
|
|
|
|
|
auto test_invalid = [r](const char* sksl, const char* expected) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(sksl));
|
|
|
|
REPORTER_ASSERT(r, !effect);
|
|
|
|
REPORTER_ASSERT(r,
|
|
|
|
errorText.contains(expected),
|
|
|
|
"Expected error message to contain \"%s\". Actual message: \"%s\"",
|
|
|
|
expected,
|
|
|
|
errorText.c_str());
|
|
|
|
};
|
|
|
|
|
2021-07-22 14:13:30 +00:00
|
|
|
// Blenders must use the 'half4 main(half4, half4)' signature. Any mixture of float4/vec4/half4
|
|
|
|
// is allowed.
|
2021-06-16 15:34:37 +00:00
|
|
|
test_valid("half4 main(half4 s, half4 d) { return s; }");
|
|
|
|
test_valid("float4 main(float4 s, float4 d) { return d; }");
|
|
|
|
test_valid("float4 main(half4 s, float4 d) { return s; }");
|
|
|
|
test_valid("half4 main(float4 s, half4 d) { return d; }");
|
|
|
|
test_valid("vec4 main(half4 s, half4 d) { return s; }");
|
|
|
|
test_valid("half4 main(vec4 s, vec4 d) { return d; }");
|
|
|
|
test_valid("vec4 main(vec4 s, vec4 d) { return s; }");
|
|
|
|
|
|
|
|
// Invalid return types
|
|
|
|
test_invalid("void main(half4 s, half4 d) {}", "'main' must return");
|
|
|
|
test_invalid("half3 main(half4 s, half4 d) { return s.rgb; }", "'main' must return");
|
|
|
|
|
|
|
|
// Invalid argument types (some are valid as shaders/color filters)
|
|
|
|
test_invalid("half4 main() { return half4(1); }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(half4 c) { return c; }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(float2 p) { return half4(1); }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(float2 p, half4 a, half4 b) { return a; }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(half4 a, half4 b, half4 c) { return a; }", "'main' parameter");
|
|
|
|
|
|
|
|
// sk_FragCoord should not be available
|
|
|
|
test_invalid("half4 main(half4 s, half4 d) { return sk_FragCoord.xy01; }",
|
|
|
|
"unknown identifier");
|
|
|
|
|
2021-07-22 14:13:30 +00:00
|
|
|
// Sampling a child shader requires that we pass explicit coords
|
|
|
|
test_valid("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s.rg); }");
|
2021-08-24 14:11:56 +00:00
|
|
|
|
|
|
|
// Sampling a colorFilter requires a color
|
2021-07-22 14:13:30 +00:00
|
|
|
test_valid("uniform colorFilter child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(d); }");
|
2021-07-22 14:13:30 +00:00
|
|
|
|
2021-08-24 14:11:56 +00:00
|
|
|
// Sampling a blender requires two colors
|
|
|
|
test_valid("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s, d); }");
|
2021-06-16 15:34:37 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 16:00:10 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectForShader, r) {
|
|
|
|
// Tests that the shader factory rejects or accepts certain SkSL constructs
|
2021-07-20 17:16:57 +00:00
|
|
|
auto test_valid = [r](const char* sksl, SkRuntimeEffect::Options options = {}) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl), options);
|
2021-04-28 19:14:09 +00:00
|
|
|
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
|
2021-04-20 16:00:10 +00:00
|
|
|
};
|
|
|
|
|
2021-07-20 17:16:57 +00:00
|
|
|
auto test_invalid = [r](const char* sksl,
|
|
|
|
const char* expected,
|
|
|
|
SkRuntimeEffect::Options options = {}) {
|
2021-04-20 16:00:10 +00:00
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl));
|
|
|
|
REPORTER_ASSERT(r, !effect);
|
|
|
|
REPORTER_ASSERT(r,
|
|
|
|
errorText.contains(expected),
|
|
|
|
"Expected error message to contain \"%s\". Actual message: \"%s\"",
|
|
|
|
expected,
|
|
|
|
errorText.c_str());
|
|
|
|
};
|
|
|
|
|
|
|
|
// Shaders must use either the 'half4 main(float2)' or 'half4 main(float2, half4)' signature
|
|
|
|
// Either color can be half4/float4/vec4, but the coords must be float2/vec2
|
|
|
|
test_valid("half4 main(float2 p) { return p.xyxy; }");
|
|
|
|
test_valid("float4 main(float2 p) { return p.xyxy; }");
|
|
|
|
test_valid("vec4 main(float2 p) { return p.xyxy; }");
|
|
|
|
test_valid("half4 main(vec2 p) { return p.xyxy; }");
|
|
|
|
test_valid("vec4 main(vec2 p) { return p.xyxy; }");
|
|
|
|
test_valid("half4 main(float2 p, half4 c) { return c; }");
|
|
|
|
test_valid("half4 main(float2 p, float4 c) { return c; }");
|
|
|
|
test_valid("half4 main(float2 p, vec4 c) { return c; }");
|
|
|
|
test_valid("float4 main(float2 p, half4 c) { return c; }");
|
|
|
|
test_valid("vec4 main(float2 p, half4 c) { return c; }");
|
|
|
|
test_valid("vec4 main(vec2 p, vec4 c) { return c; }");
|
|
|
|
|
|
|
|
// Invalid return types
|
|
|
|
test_invalid("void main(float2 p) {}", "'main' must return");
|
|
|
|
test_invalid("half3 main(float2 p) { return p.xy1; }", "'main' must return");
|
|
|
|
|
|
|
|
// Invalid argument types (some are valid as color filters, but not shaders)
|
|
|
|
test_invalid("half4 main() { return half4(1); }", "'main' parameter");
|
|
|
|
test_invalid("half4 main(half4 c) { return c; }", "'main' parameter");
|
|
|
|
|
2021-07-20 17:16:57 +00:00
|
|
|
// sk_FragCoord should be available, but only if we've enabled it via Options
|
|
|
|
test_invalid("half4 main(float2 p) { return sk_FragCoord.xy01; }",
|
|
|
|
"unknown identifier 'sk_FragCoord'");
|
|
|
|
|
|
|
|
SkRuntimeEffect::Options optionsWithFragCoord;
|
|
|
|
SkRuntimeEffectPriv::EnableFragCoord(&optionsWithFragCoord);
|
|
|
|
test_valid("half4 main(float2 p) { return sk_FragCoord.xy01; }", optionsWithFragCoord);
|
2021-04-20 16:00:10 +00:00
|
|
|
|
|
|
|
// Sampling a child shader requires that we pass explicit coords
|
|
|
|
test_valid("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { return child.eval(p); }");
|
2021-04-20 16:00:10 +00:00
|
|
|
|
2021-08-24 14:11:56 +00:00
|
|
|
// Sampling a colorFilter requires a color
|
Reland "Better first-class shader & color filter support in runtime effects"
This is a reland of adadb95a9f1ef21ccc5264c7d0bdc83b56cf91e9
... adds a temporary workaround for some Android framework code.
Original change's description:
> Better first-class shader & color filter support in runtime effects
>
> This does a few things, because they're all intertwined:
>
> 1) SkRuntimeEffect's API now includes details about children (which Skia
> stage was declared, not just the name). The factories verify that the
> declared types in the SkSL match up with the C++ types being passed.
> Today, we still only support adding children of the same type, so the
> checks are simple. Once we allow mixing types, we'll be testing the
> declared type against the actual C++ type supplied for each slot.
> 2) Adds sample variants that supply the input color to the child. This
> is now the only way to invoke a colorFilter child. Internally, we
> support passing a color when invoking a child shader, but I'm not
> exposing that. It's not clearly part of the semantics of the Skia
> pipeline, and is almost never useful. It also exposes users to
> several inconsistencies (skbug.com/11942).
> 3) Because of #2, it's possible that we can't compute a reusable program
> to filter individual colors. In that case, we don't set the constant
> output for constant input optimization, and filterColor4f falls back
> to the slower base-class implementation.
>
> Bug: skia:11813 skia:11942
> Change-Id: I06c41e1b35056e486f3163a72acf6b9535d7fed4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/401917
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Klein <mtklein@google.com>
Bug: skia:11813 skia:11942
Change-Id: I2c31b147ed86fa8c4dddefb7066bc1d07fe0d285
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404637
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-04-21 13:57:19 +00:00
|
|
|
test_valid("uniform colorFilter child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p, half4 c) { return child.eval(c); }");
|
Reland "Better first-class shader & color filter support in runtime effects"
This is a reland of adadb95a9f1ef21ccc5264c7d0bdc83b56cf91e9
... adds a temporary workaround for some Android framework code.
Original change's description:
> Better first-class shader & color filter support in runtime effects
>
> This does a few things, because they're all intertwined:
>
> 1) SkRuntimeEffect's API now includes details about children (which Skia
> stage was declared, not just the name). The factories verify that the
> declared types in the SkSL match up with the C++ types being passed.
> Today, we still only support adding children of the same type, so the
> checks are simple. Once we allow mixing types, we'll be testing the
> declared type against the actual C++ type supplied for each slot.
> 2) Adds sample variants that supply the input color to the child. This
> is now the only way to invoke a colorFilter child. Internally, we
> support passing a color when invoking a child shader, but I'm not
> exposing that. It's not clearly part of the semantics of the Skia
> pipeline, and is almost never useful. It also exposes users to
> several inconsistencies (skbug.com/11942).
> 3) Because of #2, it's possible that we can't compute a reusable program
> to filter individual colors. In that case, we don't set the constant
> output for constant input optimization, and filterColor4f falls back
> to the slower base-class implementation.
>
> Bug: skia:11813 skia:11942
> Change-Id: I06c41e1b35056e486f3163a72acf6b9535d7fed4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/401917
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Klein <mtklein@google.com>
Bug: skia:11813 skia:11942
Change-Id: I2c31b147ed86fa8c4dddefb7066bc1d07fe0d285
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404637
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-04-21 13:57:19 +00:00
|
|
|
|
2021-08-24 14:11:56 +00:00
|
|
|
// Sampling a blender requires two colors
|
|
|
|
test_valid("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p, half4 c) { return child.eval(c, c); }");
|
2021-04-20 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
|
|
|
|
|
|
|
|
void paint_canvas(SkCanvas* canvas, SkPaint* paint, const PreTestFn& preTestCallback) {
|
|
|
|
canvas->save();
|
|
|
|
if (preTestCallback) {
|
|
|
|
preTestCallback(canvas, paint);
|
|
|
|
}
|
|
|
|
canvas->drawPaint(*paint);
|
|
|
|
canvas->restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verify_2x2_surface_results(skiatest::Reporter* r,
|
|
|
|
const SkRuntimeEffect* effect,
|
|
|
|
SkSurface* surface,
|
|
|
|
std::array<GrColor, 4> expected) {
|
|
|
|
std::array<GrColor, 4> actual;
|
|
|
|
SkImageInfo info = surface->imageInfo();
|
|
|
|
if (!surface->readPixels(info, actual.data(), info.minRowBytes(), /*srcX=*/0, /*srcY=*/0)) {
|
|
|
|
REPORT_FAILURE(r, "readPixels", SkString("readPixels failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actual != expected) {
|
|
|
|
REPORT_FAILURE(r, "Runtime effect didn't match expectations",
|
|
|
|
SkStringPrintf("\n"
|
|
|
|
"Expected: [ %08x %08x %08x %08x ]\n"
|
|
|
|
"Got : [ %08x %08x %08x %08x ]\n"
|
|
|
|
"SkSL:\n%s\n",
|
|
|
|
expected[0], expected[1], expected[2], expected[3],
|
|
|
|
actual[0], actual[1], actual[2], actual[3],
|
|
|
|
effect->source().c_str()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 18:19:58 +00:00
|
|
|
class TestEffect {
|
|
|
|
public:
|
2020-07-22 14:19:02 +00:00
|
|
|
TestEffect(skiatest::Reporter* r, sk_sp<SkSurface> surface)
|
|
|
|
: fReporter(r), fSurface(std::move(surface)) {}
|
|
|
|
|
2020-11-06 15:42:51 +00:00
|
|
|
void build(const char* src) {
|
2021-07-20 17:16:57 +00:00
|
|
|
SkRuntimeEffect::Options options;
|
|
|
|
SkRuntimeEffectPriv::EnableFragCoord(&options);
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src), options);
|
2020-01-08 18:19:58 +00:00
|
|
|
if (!effect) {
|
2020-07-22 14:19:02 +00:00
|
|
|
REPORT_FAILURE(fReporter, "effect",
|
2020-01-08 18:19:58 +00:00
|
|
|
SkStringPrintf("Effect didn't compile: %s", errorText.c_str()));
|
|
|
|
return;
|
|
|
|
}
|
2020-04-15 18:18:13 +00:00
|
|
|
fBuilder.init(std::move(effect));
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
SkRuntimeShaderBuilder::BuilderUniform uniform(const char* name) {
|
|
|
|
return fBuilder->uniform(name);
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
2021-06-18 14:14:14 +00:00
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
SkRuntimeShaderBuilder::BuilderChild child(const char* name) {
|
|
|
|
return fBuilder->child(name);
|
|
|
|
}
|
2020-01-08 18:19:58 +00:00
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
|
2021-06-17 02:37:15 +00:00
|
|
|
auto shader = fBuilder->makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
|
2020-01-08 18:19:58 +00:00
|
|
|
if (!shader) {
|
2020-07-22 14:19:02 +00:00
|
|
|
REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader"));
|
2020-01-08 18:19:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
SkCanvas* canvas = fSurface->getCanvas();
|
2020-01-08 18:19:58 +00:00
|
|
|
SkPaint paint;
|
|
|
|
paint.setShader(std::move(shader));
|
|
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
paint_canvas(canvas, &paint, preTestCallback);
|
|
|
|
|
|
|
|
verify_2x2_surface_results(fReporter, fBuilder->effect(), fSurface.get(), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
|
|
|
|
this->test({expected, expected, expected, expected}, preTestCallback);
|
|
|
|
}
|
2020-07-22 14:19:02 +00:00
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
private:
|
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
sk_sp<SkSurface> fSurface;
|
|
|
|
SkTLazy<SkRuntimeShaderBuilder> fBuilder;
|
|
|
|
};
|
|
|
|
|
|
|
|
class TestBlend {
|
|
|
|
public:
|
|
|
|
TestBlend(skiatest::Reporter* r, sk_sp<SkSurface> surface)
|
|
|
|
: fReporter(r), fSurface(std::move(surface)) {}
|
|
|
|
|
|
|
|
void build(const char* src) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(src));
|
|
|
|
if (!effect) {
|
|
|
|
REPORT_FAILURE(fReporter, "effect",
|
|
|
|
SkStringPrintf("Effect didn't compile: %s", errorText.c_str()));
|
2020-01-08 18:19:58 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-06-18 14:14:14 +00:00
|
|
|
fBuilder.init(std::move(effect));
|
|
|
|
}
|
|
|
|
|
|
|
|
SkRuntimeBlendBuilder::BuilderUniform uniform(const char* name) {
|
|
|
|
return fBuilder->uniform(name);
|
|
|
|
}
|
2020-01-08 18:19:58 +00:00
|
|
|
|
2021-07-22 14:13:30 +00:00
|
|
|
SkRuntimeBlendBuilder::BuilderChild child(const char* name) {
|
|
|
|
return fBuilder->child(name);
|
|
|
|
}
|
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
|
|
|
|
auto blender = fBuilder->makeBlender();
|
|
|
|
if (!blender) {
|
|
|
|
REPORT_FAILURE(fReporter, "blender", SkString("Effect didn't produce a blender"));
|
|
|
|
return;
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
2021-06-18 14:14:14 +00:00
|
|
|
|
|
|
|
SkCanvas* canvas = fSurface->getCanvas();
|
|
|
|
SkPaint paint;
|
2021-07-06 15:29:45 +00:00
|
|
|
paint.setBlender(std::move(blender));
|
2021-06-18 14:14:14 +00:00
|
|
|
paint.setColor(SK_ColorGRAY);
|
|
|
|
|
|
|
|
paint_canvas(canvas, &paint, preTestCallback);
|
|
|
|
|
|
|
|
verify_2x2_surface_results(fReporter, fBuilder->effect(), fSurface.get(), expected);
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
|
2021-06-18 14:14:14 +00:00
|
|
|
this->test({expected, expected, expected, expected}, preTestCallback);
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-06-18 14:14:14 +00:00
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
sk_sp<SkSurface> fSurface;
|
|
|
|
SkTLazy<SkRuntimeBlendBuilder> fBuilder;
|
2020-01-08 18:19:58 +00:00
|
|
|
};
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
// Produces a 2x2 bitmap shader, with opaque colors:
|
|
|
|
// [ Red, Green ]
|
|
|
|
// [ Blue, White ]
|
|
|
|
static sk_sp<SkShader> make_RGBW_shader() {
|
|
|
|
SkBitmap bmp;
|
|
|
|
bmp.allocPixels(SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
|
|
|
|
SkIRect topLeft = SkIRect::MakeWH(1, 1);
|
|
|
|
bmp.pixmap().erase(SK_ColorRED, topLeft);
|
|
|
|
bmp.pixmap().erase(SK_ColorGREEN, topLeft.makeOffset(1, 0));
|
|
|
|
bmp.pixmap().erase(SK_ColorBLUE, topLeft.makeOffset(0, 1));
|
|
|
|
bmp.pixmap().erase(SK_ColorWHITE, topLeft.makeOffset(1, 1));
|
2020-12-12 16:18:31 +00:00
|
|
|
return bmp.makeShader(SkSamplingOptions());
|
2020-07-22 14:19:02 +00:00
|
|
|
}
|
|
|
|
|
2020-07-23 17:54:35 +00:00
|
|
|
static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrRecordingContext* rContext) {
|
2020-01-08 18:19:58 +00:00
|
|
|
SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
2020-07-23 17:54:35 +00:00
|
|
|
sk_sp<SkSurface> surface = rContext
|
|
|
|
? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
|
|
|
|
: SkSurface::MakeRaster(info);
|
2020-01-08 18:19:58 +00:00
|
|
|
REPORTER_ASSERT(r, surface);
|
2020-07-22 14:19:02 +00:00
|
|
|
TestEffect effect(r, surface);
|
2020-01-08 18:19:58 +00:00
|
|
|
|
2020-01-10 15:05:24 +00:00
|
|
|
using float4 = std::array<float, 4>;
|
2021-04-01 13:56:07 +00:00
|
|
|
using int4 = std::array<int, 4>;
|
2020-01-10 15:05:24 +00:00
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
// Local coords
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("half4 main(float2 p) { return half4(half2(p - 0.5), 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-07-22 14:19:02 +00:00
|
|
|
|
|
|
|
// Use of a simple uniform. (Draw twice with two values to ensure it's updated).
|
2021-04-21 19:57:27 +00:00
|
|
|
effect.build("uniform float4 gColor; half4 main(float2 p) { return half4(gColor); }");
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
effect.uniform("gColor") = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.test(0xFFBF4000);
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
effect.uniform("gColor") = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.test(0x7F00007F); // Tests that we clamp to valid premul
|
|
|
|
|
2021-04-01 13:56:07 +00:00
|
|
|
// Same, with integer uniforms
|
2021-04-21 19:57:27 +00:00
|
|
|
effect.build("uniform int4 gColor; half4 main(float2 p) { return half4(gColor) / 255.0; }");
|
2021-04-01 13:56:07 +00:00
|
|
|
effect.uniform("gColor") = int4{ 0x00, 0x40, 0xBF, 0xFF };
|
|
|
|
effect.test(0xFFBF4000);
|
|
|
|
effect.uniform("gColor") = int4{ 0xFF, 0x00, 0x00, 0x7F };
|
|
|
|
effect.test(0x7F00007F); // Tests that we clamp to valid premul
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
// Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords.
|
|
|
|
// Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to
|
|
|
|
// make sure we're not saturating unexpectedly.
|
2021-04-21 19:57:27 +00:00
|
|
|
effect.build(
|
|
|
|
"half4 main(float2 p) { return half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F},
|
2020-07-22 14:19:02 +00:00
|
|
|
[](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); });
|
|
|
|
|
2020-09-02 17:45:47 +00:00
|
|
|
// Runtime effects should use relaxed precision rules by default
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("half4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-09-02 17:45:47 +00:00
|
|
|
|
2020-11-06 15:42:51 +00:00
|
|
|
// ... and support *returning* float4 (aka vec4), not just half4
|
|
|
|
effect.build("float4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("vec4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-10-13 13:34:23 +00:00
|
|
|
|
2020-11-11 14:18:02 +00:00
|
|
|
// Mutating coords should work. (skbug.com/10918)
|
|
|
|
effect.build("vec4 main(vec2 p) { p -= 0.5; return vec4(p, 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-11-11 14:18:02 +00:00
|
|
|
effect.build("void moveCoords(inout vec2 p) { p -= 0.5; }"
|
|
|
|
"vec4 main(vec2 p) { moveCoords(p); return vec4(p, 0, 1); }");
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
|
2020-11-11 14:18:02 +00:00
|
|
|
|
2020-07-23 17:28:14 +00:00
|
|
|
//
|
|
|
|
// Sampling children
|
|
|
|
//
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
// Sampling a null child should return the paint color
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { return child.eval(p); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = nullptr;
|
|
|
|
effect.test(0xFF00FFFF,
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
|
|
|
|
|
|
|
|
sk_sp<SkShader> rgbwShader = make_RGBW_shader();
|
|
|
|
|
2021-04-21 19:57:27 +00:00
|
|
|
// Sampling a simple child at our coordinates
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { return child.eval(p); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF});
|
2020-07-22 14:19:02 +00:00
|
|
|
|
|
|
|
// Sampling with explicit coordinates (reflecting about the diagonal)
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { return child.eval(p.yx); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
2021-06-18 14:14:14 +00:00
|
|
|
effect.test({0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF});
|
2020-07-22 14:19:02 +00:00
|
|
|
|
2021-09-08 15:16:29 +00:00
|
|
|
// Bind an image shader, but don't use it - ensure that we don't assert or generate bad shaders.
|
|
|
|
// (skbug.com/12429)
|
|
|
|
effect.build("uniform shader child;"
|
|
|
|
"half4 main(float2 p) { return half4(0, 1, 0, 1); }");
|
|
|
|
effect.child("child") = rgbwShader;
|
|
|
|
effect.test(0xFF00FF00);
|
|
|
|
|
2020-07-23 17:28:14 +00:00
|
|
|
//
|
|
|
|
// Helper functions
|
|
|
|
//
|
|
|
|
|
|
|
|
// Test case for inlining in the pipeline-stage and fragment-shader passes (skbug.com/10526):
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("float2 helper(float2 x) { return x + 1; }"
|
|
|
|
"half4 main(float2 p) { float2 v = helper(p); return half4(half2(v), 0, 1); }");
|
2020-07-23 17:28:14 +00:00
|
|
|
effect.test(0xFF00FFFF);
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeEffectSimple, r) {
|
|
|
|
test_RuntimeEffect_Shaders(r, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectSimple_GPU, r, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_RuntimeEffect_Shaders(r, ctxInfo.directContext());
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
2020-08-27 14:51:22 +00:00
|
|
|
|
2021-06-18 14:14:14 +00:00
|
|
|
static void test_RuntimeEffect_Blenders(skiatest::Reporter* r, GrRecordingContext* rContext) {
|
|
|
|
SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
|
|
sk_sp<SkSurface> surface = rContext
|
|
|
|
? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
|
|
|
|
: SkSurface::MakeRaster(info);
|
|
|
|
REPORTER_ASSERT(r, surface);
|
|
|
|
TestBlend effect(r, surface);
|
|
|
|
|
2021-07-22 14:13:30 +00:00
|
|
|
using float2 = std::array<float, 2>;
|
2021-06-18 14:14:14 +00:00
|
|
|
using float4 = std::array<float, 4>;
|
|
|
|
using int4 = std::array<int, 4>;
|
|
|
|
|
|
|
|
// Use of a simple uniform. (Draw twice with two values to ensure it's updated).
|
|
|
|
effect.build("uniform float4 gColor; half4 main(half4 s, half4 d) { return half4(gColor); }");
|
|
|
|
effect.uniform("gColor") = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
|
|
|
|
effect.test(0xFFBF4000);
|
|
|
|
effect.uniform("gColor") = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
|
|
|
|
effect.test(0x7F0000FF); // Unlike SkShaders, we don't clamp here
|
|
|
|
|
|
|
|
// Same, with integer uniforms
|
|
|
|
effect.build("uniform int4 gColor;"
|
|
|
|
"half4 main(half4 s, half4 d) { return half4(gColor) / 255.0; }");
|
|
|
|
effect.uniform("gColor") = int4{ 0x00, 0x40, 0xBF, 0xFF };
|
|
|
|
effect.test(0xFFBF4000);
|
|
|
|
effect.uniform("gColor") = int4{ 0xFF, 0x00, 0x00, 0x7F };
|
|
|
|
effect.test(0x7F0000FF); // Unlike SkShaders, we don't clamp here
|
|
|
|
|
|
|
|
// Verify that mutating the source and destination colors is allowed
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { s += d; d += s; return half4(1); }");
|
|
|
|
effect.test(0xFFFFFFFF);
|
|
|
|
|
|
|
|
// Verify that we can write out the source color (ignoring the dest color)
|
|
|
|
// This is equivalent to the kSrc blend mode.
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { return s; }");
|
|
|
|
effect.test(0xFF888888);
|
|
|
|
|
|
|
|
// Fill the destination with a variety of colors (using the RGBW shader)
|
2021-07-30 15:20:19 +00:00
|
|
|
SkPaint rgbwPaint;
|
|
|
|
rgbwPaint.setShader(make_RGBW_shader());
|
|
|
|
rgbwPaint.setBlendMode(SkBlendMode::kSrc);
|
|
|
|
surface->getCanvas()->drawPaint(rgbwPaint);
|
2021-06-18 14:14:14 +00:00
|
|
|
|
|
|
|
// Verify that we can read back the dest color exactly as-is (ignoring the source color)
|
|
|
|
// This is equivalent to the kDst blend mode.
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { return d; }");
|
|
|
|
effect.test({0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF});
|
|
|
|
|
|
|
|
// Verify that we can invert the destination color (including the alpha channel).
|
|
|
|
// The expected outputs are the exact inverse of the previous test.
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { return half4(1) - d; }");
|
|
|
|
effect.test({0x00FFFF00, 0x00FF00FF, 0x0000FFFF, 0x00000000});
|
|
|
|
|
|
|
|
// Verify that color values are clamped to 0 and 1.
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { return half4(-1); }");
|
|
|
|
effect.test(0x00000000);
|
|
|
|
effect.build("half4 main(half4 s, half4 d) { return half4(2); }");
|
|
|
|
effect.test(0xFFFFFFFF);
|
2021-07-22 14:13:30 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Sampling children
|
|
|
|
//
|
|
|
|
|
2021-07-30 15:20:19 +00:00
|
|
|
// Sampling a null shader/color filter should return the paint color.
|
2021-07-22 14:13:30 +00:00
|
|
|
effect.build("uniform shader child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s.rg); }");
|
2021-07-22 14:13:30 +00:00
|
|
|
effect.child("child") = nullptr;
|
|
|
|
effect.test(0xFF00FFFF,
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
|
|
|
|
|
2021-07-30 15:20:19 +00:00
|
|
|
effect.build("uniform colorFilter child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s); }");
|
2021-07-30 15:20:19 +00:00
|
|
|
effect.child("child") = nullptr;
|
|
|
|
effect.test(0xFF00FFFF,
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
|
|
|
|
|
|
|
|
// Sampling a null blender should do a src-over blend. Draw 50% black over RGBW to verify this.
|
|
|
|
surface->getCanvas()->drawPaint(rgbwPaint);
|
|
|
|
effect.build("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s, d); }");
|
2021-07-30 15:20:19 +00:00
|
|
|
effect.child("child") = nullptr;
|
|
|
|
effect.test({0xFF000080, 0xFF008000, 0xFF800000, 0xFF808080},
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({0.0f, 0.0f, 0.0f, 0.497f}); });
|
|
|
|
|
2021-07-22 14:13:30 +00:00
|
|
|
// Sampling a shader at various coordinates
|
|
|
|
effect.build("uniform shader child;"
|
|
|
|
"uniform half2 pos;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(pos); }");
|
2021-07-22 14:13:30 +00:00
|
|
|
effect.child("child") = make_RGBW_shader();
|
|
|
|
effect.uniform("pos") = float2{0, 0};
|
|
|
|
effect.test(0xFF0000FF);
|
|
|
|
|
|
|
|
effect.uniform("pos") = float2{1, 0};
|
|
|
|
effect.test(0xFF00FF00);
|
|
|
|
|
|
|
|
effect.uniform("pos") = float2{0, 1};
|
|
|
|
effect.test(0xFFFF0000);
|
|
|
|
|
|
|
|
effect.uniform("pos") = float2{1, 1};
|
|
|
|
effect.test(0xFFFFFFFF);
|
|
|
|
|
|
|
|
// Sampling a color filter
|
|
|
|
effect.build("uniform colorFilter child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(half4(1)); }");
|
2021-07-22 14:13:30 +00:00
|
|
|
effect.child("child") = SkColorFilters::Blend(0xFF012345, SkBlendMode::kSrc);
|
|
|
|
effect.test(0xFF452301);
|
2021-07-30 15:20:19 +00:00
|
|
|
|
|
|
|
// Sampling a built-in blender
|
|
|
|
surface->getCanvas()->drawPaint(rgbwPaint);
|
|
|
|
effect.build("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s, d); }");
|
2021-07-30 15:20:19 +00:00
|
|
|
effect.child("child") = SkBlender::Mode(SkBlendMode::kPlus);
|
|
|
|
effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
|
|
|
|
|
|
|
|
// Sampling a runtime-effect blender
|
|
|
|
surface->getCanvas()->drawPaint(rgbwPaint);
|
|
|
|
effect.build("uniform blender child;"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(half4 s, half4 d) { return child.eval(s, d); }");
|
2021-07-30 15:20:19 +00:00
|
|
|
effect.child("child") = SkBlenders::Arithmetic(0, 1, 1, 0, /*enforcePremul=*/false);
|
|
|
|
effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
|
|
|
|
[](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
|
2021-06-18 14:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {
|
2021-06-18 14:15:35 +00:00
|
|
|
test_RuntimeEffect_Blenders(r, /*rContext=*/nullptr);
|
2021-06-18 14:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffect_Blender_GPU, r, ctxInfo) {
|
|
|
|
test_RuntimeEffect_Blenders(r, ctxInfo.directContext());
|
|
|
|
}
|
|
|
|
|
2020-08-27 14:51:22 +00:00
|
|
|
DEF_TEST(SkRuntimeShaderBuilderReuse, r) {
|
|
|
|
const char* kSource = R"(
|
|
|
|
uniform half x;
|
2021-04-21 19:57:27 +00:00
|
|
|
half4 main(float2 p) { return half4(x); }
|
2020-08-27 14:51:22 +00:00
|
|
|
)";
|
|
|
|
|
2021-04-21 19:57:27 +00:00
|
|
|
sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForShader(SkString(kSource)).effect;
|
2020-08-27 14:51:22 +00:00
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
|
|
|
|
// Test passes if this sequence doesn't assert. skbug.com/10667
|
|
|
|
SkRuntimeShaderBuilder b(std::move(effect));
|
|
|
|
b.uniform("x") = 0.0f;
|
2021-06-17 02:37:15 +00:00
|
|
|
auto shader_0 = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
|
2020-08-27 14:51:22 +00:00
|
|
|
|
|
|
|
b.uniform("x") = 1.0f;
|
2021-06-17 02:37:15 +00:00
|
|
|
auto shader_1 = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/true);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeBlendBuilderReuse, r) {
|
|
|
|
const char* kSource = R"(
|
|
|
|
uniform half x;
|
|
|
|
half4 main(half4 s, half4 d) { return half4(x); }
|
|
|
|
)";
|
|
|
|
|
|
|
|
sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForBlender(SkString(kSource)).effect;
|
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
|
|
|
|
// We should be able to construct multiple SkBlenders in a row without asserting.
|
|
|
|
SkRuntimeBlendBuilder b(std::move(effect));
|
|
|
|
for (float x = 0.0f; x <= 2.0f; x += 2.0f) {
|
|
|
|
b.uniform("x") = x;
|
|
|
|
sk_sp<SkBlender> blender = b.makeBlender();
|
|
|
|
}
|
2020-08-27 14:51:22 +00:00
|
|
|
}
|
2020-09-30 17:26:43 +00:00
|
|
|
|
2021-01-14 13:30:52 +00:00
|
|
|
DEF_TEST(SkRuntimeShaderBuilderSetUniforms, r) {
|
|
|
|
const char* kSource = R"(
|
|
|
|
uniform half x;
|
|
|
|
uniform vec2 offset;
|
2021-04-21 19:57:27 +00:00
|
|
|
half4 main(float2 p) { return half4(x); }
|
2021-01-14 13:30:52 +00:00
|
|
|
)";
|
|
|
|
|
2021-04-21 19:57:27 +00:00
|
|
|
sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForShader(SkString(kSource)).effect;
|
2021-01-14 13:30:52 +00:00
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
|
|
|
|
SkRuntimeShaderBuilder b(std::move(effect));
|
|
|
|
|
|
|
|
// Test passes if this sequence doesn't assert.
|
|
|
|
float x = 1.0f;
|
|
|
|
REPORTER_ASSERT(r, b.uniform("x").set(&x, 1));
|
|
|
|
|
|
|
|
// add extra value to ensure that set doesn't try to use sizeof(array)
|
|
|
|
float origin[] = { 2.0f, 3.0f, 4.0f };
|
|
|
|
REPORTER_ASSERT(r, b.uniform("offset").set<float>(origin, 2));
|
|
|
|
|
|
|
|
#ifndef SK_DEBUG
|
|
|
|
REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 1));
|
|
|
|
REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 3));
|
|
|
|
#endif
|
|
|
|
|
2021-06-17 02:37:15 +00:00
|
|
|
auto shader = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
|
2021-01-14 13:30:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-30 17:26:43 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectThreaded, r) {
|
|
|
|
// SkRuntimeEffect uses a single compiler instance, but it's mutex locked.
|
|
|
|
// This tests that we can safely use it from more than one thread, and also
|
|
|
|
// that programs don't refer to shared structures owned by the compiler.
|
|
|
|
// skbug.com/10589
|
2021-04-21 19:57:27 +00:00
|
|
|
static constexpr char kSource[] = "half4 main(float2 p) { return sk_FragCoord.xyxy; }";
|
2020-09-30 17:26:43 +00:00
|
|
|
|
|
|
|
std::thread threads[16];
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
thread = std::thread([r]() {
|
2021-07-20 17:16:57 +00:00
|
|
|
SkRuntimeEffect::Options options;
|
|
|
|
SkRuntimeEffectPriv::EnableFragCoord(&options);
|
|
|
|
auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(kSource), options);
|
2020-09-30 17:26:43 +00:00
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
}
|
2021-02-06 15:13:01 +00:00
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeColorFilterSingleColor, r) {
|
|
|
|
// Test runtime colorfilters support filterColor4f().
|
2021-04-21 19:57:27 +00:00
|
|
|
auto [effect, err] =
|
|
|
|
SkRuntimeEffect::MakeForColorFilter(SkString{"half4 main(half4 c) { return c*c; }"});
|
2021-02-06 15:13:01 +00:00
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
REPORTER_ASSERT(r, err.isEmpty());
|
|
|
|
|
2021-04-21 19:57:27 +00:00
|
|
|
sk_sp<SkColorFilter> cf = effect->makeColorFilter(SkData::MakeEmpty());
|
2021-02-06 15:13:01 +00:00
|
|
|
REPORTER_ASSERT(r, cf);
|
|
|
|
|
|
|
|
SkColor4f c = cf->filterColor4f({0.25, 0.5, 0.75, 1.0},
|
|
|
|
sk_srgb_singleton(), sk_srgb_singleton());
|
|
|
|
REPORTER_ASSERT(r, c.fR == 0.0625f);
|
|
|
|
REPORTER_ASSERT(r, c.fG == 0.25f);
|
|
|
|
REPORTER_ASSERT(r, c.fB == 0.5625f);
|
|
|
|
REPORTER_ASSERT(r, c.fA == 1.0f);
|
|
|
|
}
|
2021-02-10 15:19:27 +00:00
|
|
|
|
|
|
|
static void test_RuntimeEffectStructNameReuse(skiatest::Reporter* r, GrRecordingContext* rContext) {
|
|
|
|
// Test that two different runtime effects can reuse struct names in a single paint operation
|
2021-04-21 19:57:27 +00:00
|
|
|
auto [childEffect, err] = SkRuntimeEffect::MakeForShader(SkString(
|
2021-02-10 15:19:27 +00:00
|
|
|
"uniform shader paint;"
|
|
|
|
"struct S { half4 rgba; };"
|
|
|
|
"void process(inout S s) { s.rgba.rgb *= 0.5; }"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { S s; s.rgba = paint.eval(p); process(s); return s.rgba; }"
|
2021-02-10 15:19:27 +00:00
|
|
|
));
|
|
|
|
REPORTER_ASSERT(r, childEffect, "%s\n", err.c_str());
|
|
|
|
sk_sp<SkShader> nullChild = nullptr;
|
|
|
|
sk_sp<SkShader> child = childEffect->makeShader(/*uniforms=*/nullptr, &nullChild,
|
|
|
|
/*childCount=*/1, /*localMatrix=*/nullptr,
|
|
|
|
/*isOpaque=*/false);
|
|
|
|
|
|
|
|
SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
|
|
sk_sp<SkSurface> surface = rContext
|
|
|
|
? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
|
|
|
|
: SkSurface::MakeRaster(info);
|
|
|
|
REPORTER_ASSERT(r, surface);
|
|
|
|
|
|
|
|
TestEffect effect(r, surface);
|
|
|
|
effect.build(
|
|
|
|
"uniform shader child;"
|
|
|
|
"struct S { float2 coord; };"
|
|
|
|
"void process(inout S s) { s.coord = s.coord.yx; }"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 p) { S s; s.coord = p; process(s); return child.eval(s.coord); "
|
2021-02-10 15:19:27 +00:00
|
|
|
"}");
|
|
|
|
effect.child("child") = child;
|
|
|
|
effect.test(0xFF00407F, [](SkCanvas*, SkPaint* paint) {
|
|
|
|
paint->setColor4f({0.99608f, 0.50196f, 0.0f, 1.0f});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeStructNameReuse, r) {
|
|
|
|
test_RuntimeEffectStructNameReuse(r, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeStructNameReuse_GPU, r, ctxInfo) {
|
|
|
|
test_RuntimeEffectStructNameReuse(r, ctxInfo.directContext());
|
|
|
|
}
|
2021-02-16 18:00:29 +00:00
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeColorFilterFlags, r) {
|
|
|
|
{ // Here's a non-trivial filter that doesn't change alpha.
|
2021-04-21 19:57:27 +00:00
|
|
|
auto [effect, err] = SkRuntimeEffect::MakeForColorFilter(SkString{
|
2021-04-14 13:36:49 +00:00
|
|
|
"half4 main(half4 color) { return color + half4(1,1,1,0); }"});
|
2021-02-16 18:00:29 +00:00
|
|
|
REPORTER_ASSERT(r, effect && err.isEmpty());
|
2021-04-14 13:36:49 +00:00
|
|
|
sk_sp<SkColorFilter> filter = effect->makeColorFilter(SkData::MakeEmpty());
|
2021-02-16 18:00:29 +00:00
|
|
|
REPORTER_ASSERT(r, filter && filter->isAlphaUnchanged());
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Here's one that definitely changes alpha.
|
2021-04-21 19:57:27 +00:00
|
|
|
auto [effect, err] = SkRuntimeEffect::MakeForColorFilter(SkString{
|
2021-04-14 13:36:49 +00:00
|
|
|
"half4 main(half4 color) { return color + half4(0,0,0,4); }"});
|
2021-02-16 18:00:29 +00:00
|
|
|
REPORTER_ASSERT(r, effect && err.isEmpty());
|
2021-04-14 13:36:49 +00:00
|
|
|
sk_sp<SkColorFilter> filter = effect->makeColorFilter(SkData::MakeEmpty());
|
2021-02-16 18:00:29 +00:00
|
|
|
REPORTER_ASSERT(r, filter && !filter->isAlphaUnchanged());
|
|
|
|
}
|
|
|
|
}
|
2021-04-27 13:10:10 +00:00
|
|
|
|
2021-05-21 20:23:06 +00:00
|
|
|
DEF_TEST(SkRuntimeShaderSampleCoords, r) {
|
|
|
|
// This test verifies that we detect calls to sample where the coords are the same as those
|
|
|
|
// passed to main. In those cases, it's safe to turn the "explicit" sampling into "passthrough"
|
|
|
|
// sampling. This optimization is implemented very conservatively.
|
|
|
|
//
|
|
|
|
// It also checks that we correctly set the "referencesSampleCoords" bit on the runtime effect
|
|
|
|
// FP, depending on how the coords parameter to main is used.
|
|
|
|
|
|
|
|
auto test = [&](const char* src, bool expectExplicit, bool expectReferencesSampleCoords) {
|
2021-04-27 13:10:10 +00:00
|
|
|
auto [effect, err] =
|
|
|
|
SkRuntimeEffect::MakeForShader(SkStringPrintf("uniform shader child; %s", src));
|
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
|
|
|
|
auto child = GrFragmentProcessor::MakeColor({ 1, 1, 1, 1 });
|
2021-06-16 21:10:21 +00:00
|
|
|
auto fp = GrSkSLFP::Make(effect, "test_fp", /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kNone,
|
|
|
|
"child", std::move(child));
|
2021-04-27 13:10:10 +00:00
|
|
|
REPORTER_ASSERT(r, fp);
|
|
|
|
|
2021-08-02 16:37:14 +00:00
|
|
|
REPORTER_ASSERT(r, fp->childProcessor(0)->sampleUsage().isExplicit() == expectExplicit);
|
|
|
|
REPORTER_ASSERT(r, fp->usesSampleCoords() == expectReferencesSampleCoords);
|
2021-04-27 13:10:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Cases where our optimization is valid, and works:
|
|
|
|
|
2021-05-24 13:52:39 +00:00
|
|
|
// Direct use of passed-in coords. Here, the only use of sample coords is for a sample call
|
|
|
|
// converted to passthrough, so referenceSampleCoords is *false*, despite appearing in main.
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { return child.eval(xy); }", false, false);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Sample with passed-in coords, read (but don't write) sample coords elsewhere
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { return child.eval(xy) + sin(xy.x); }", false, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
|
|
|
|
// Cases where our optimization is not valid, and does not happen:
|
|
|
|
|
|
|
|
// Sampling with values completely unrelated to passed-in coords
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { return child.eval(float2(0, 0)); }", true, false);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Use of expression involving passed in coords
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { return child.eval(xy * 0.5); }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Use of coords after modification
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { xy *= 2; return child.eval(xy); }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Use of coords after modification via out-param call
|
|
|
|
test("void adjust(inout float2 xy) { xy *= 2; }"
|
2021-09-02 13:26:27 +00:00
|
|
|
"half4 main(float2 xy) { adjust(xy); return child.eval(xy); }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
|
|
|
|
// There should (must) not be any false-positive cases. There are false-negatives.
|
|
|
|
// In all of these cases, our optimization would be valid, but does not happen:
|
|
|
|
|
|
|
|
// Direct use of passed-in coords, modified after use
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { half4 c = child.eval(xy); xy *= 2; return c; }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Passed-in coords copied to a temp variable
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 main(float2 xy) { float2 p = xy; return child.eval(p); }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
// Use of coords passed to helper function
|
2021-09-02 13:26:27 +00:00
|
|
|
test("half4 helper(float2 xy) { return child.eval(xy); }"
|
2021-05-21 20:23:06 +00:00
|
|
|
"half4 main(float2 xy) { return helper(xy); }", true, true);
|
2021-04-27 13:10:10 +00:00
|
|
|
}
|
2021-06-10 18:27:53 +00:00
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSkSLFP_Specialized, r, ctxInfo) {
|
|
|
|
struct FpAndKey {
|
|
|
|
std::unique_ptr<GrFragmentProcessor> fp;
|
|
|
|
SkTArray<uint32_t, true> key;
|
|
|
|
};
|
|
|
|
|
2021-06-18 13:15:44 +00:00
|
|
|
// Constant color, but with a similar option to GrFragmentProcessor::OverrideInput
|
2021-06-10 18:27:53 +00:00
|
|
|
// specialize decides if the color is inserted in the SkSL as a literal, or left as a uniform
|
|
|
|
auto make_color_fp = [&](SkPMColor4f color, bool specialize) {
|
|
|
|
auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, R"(
|
|
|
|
uniform half4 color;
|
|
|
|
half4 main(float2 xy) { return color; }
|
|
|
|
)");
|
|
|
|
FpAndKey result;
|
2021-06-15 18:36:17 +00:00
|
|
|
result.fp = GrSkSLFP::Make(std::move(effect), "color_fp", /*inputFP=*/nullptr,
|
2021-06-16 21:10:21 +00:00
|
|
|
GrSkSLFP::OptFlags::kNone,
|
2021-06-15 18:36:17 +00:00
|
|
|
"color", GrSkSLFP::SpecializeIf(specialize, color));
|
2021-06-10 18:27:53 +00:00
|
|
|
GrProcessorKeyBuilder builder(&result.key);
|
2021-08-06 19:33:58 +00:00
|
|
|
result.fp->addToKey(*ctxInfo.directContext()->priv().caps()->shaderCaps(), &builder);
|
2021-06-10 18:27:53 +00:00
|
|
|
builder.flush();
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
FpAndKey uRed = make_color_fp({1, 0, 0, 1}, false),
|
|
|
|
uGreen = make_color_fp({0, 1, 0, 1}, false),
|
|
|
|
sRed = make_color_fp({1, 0, 0, 1}, true),
|
|
|
|
sGreen = make_color_fp({0, 1, 0, 1}, true);
|
|
|
|
|
|
|
|
// uRed and uGreen should have the same key - they just have different uniforms
|
|
|
|
SkASSERT(uRed.key == uGreen.key);
|
|
|
|
// sRed and sGreen should have keys that are different from the uniform case, and each other
|
|
|
|
SkASSERT(sRed.key != uRed.key);
|
|
|
|
SkASSERT(sGreen.key != uRed.key);
|
|
|
|
SkASSERT(sRed.key != sGreen.key);
|
|
|
|
}
|