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"
|
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"
|
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"
|
2020-04-15 18:18:13 +00:00
|
|
|
#include "src/core/SkTLazy.h"
|
2020-07-22 14:19:02 +00:00
|
|
|
#include "src/gpu/GrColor.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) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::Make(SkString(src));
|
|
|
|
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
|
|
|
|
2020-11-10 21:36:01 +00:00
|
|
|
#define EMPTY_MAIN "half4 main() { return half4(0); }"
|
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_FPOnly, r) {
|
2019-12-19 20:44:56 +00:00
|
|
|
// Features that are only allowed in .fp files (key, in uniform, ctype, when, tracked).
|
|
|
|
// Ensure that these fail, and the error messages contain the relevant keyword.
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "layout(key) in bool Input;" EMPTY_MAIN, "key");
|
|
|
|
test_invalid_effect(r, "in uniform float Input;" EMPTY_MAIN, "in uniform");
|
|
|
|
test_invalid_effect(r, "layout(ctype=SkRect) float4 Input;" EMPTY_MAIN, "ctype");
|
|
|
|
test_invalid_effect(r, "in bool Flag; "
|
|
|
|
"layout(when=Flag) uniform float Input;" EMPTY_MAIN, "when");
|
|
|
|
test_invalid_effect(r, "layout(tracked) uniform float Input;" EMPTY_MAIN, "tracked");
|
|
|
|
}
|
2019-12-19 20:44:56 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_LimitedUniformTypes, r) {
|
2020-11-06 16:45:36 +00:00
|
|
|
// Runtime SkSL supports a limited set of uniform types. No bool, or int, for example:
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "uniform bool b;" EMPTY_MAIN, "uniform");
|
|
|
|
test_invalid_effect(r, "uniform int i;" 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_MarkerRequiresFloat4x4, 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
|
|
|
// 'marker' is only permitted on float4x4 uniforms
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r,
|
|
|
|
"layout(marker=local_to_world) uniform float3x3 localToWorld;" EMPTY_MAIN,
|
|
|
|
"float4x4");
|
|
|
|
}
|
2020-07-16 19:29:15 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_UndefinedFunction, r) {
|
|
|
|
test_invalid_effect(r, "half4 missing(); half4 main() { return missing(); }",
|
|
|
|
"undefined function");
|
|
|
|
}
|
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_ShaderLimitations, r) {
|
2020-07-21 13:39:27 +00:00
|
|
|
// Various places that shaders (fragmentProcessors) should not be allowed
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "half4 main() { shader child; return sample(child); }",
|
|
|
|
"must be global");
|
|
|
|
test_invalid_effect(r, "uniform shader child; half4 helper(shader fp) { return sample(fp); }"
|
|
|
|
"half4 main() { return helper(child); }",
|
|
|
|
"parameter");
|
|
|
|
test_invalid_effect(r, "uniform shader child; shader get_child() { return child; }"
|
|
|
|
"half4 main() { return sample(get_child()); }",
|
|
|
|
"return");
|
|
|
|
test_invalid_effect(r, "uniform shader child;"
|
|
|
|
"half4 main() { return sample(shader(child)); }",
|
|
|
|
"construct");
|
|
|
|
test_invalid_effect(r, "uniform shader child1; uniform shader child2;"
|
|
|
|
"half4 main(float2 p) { return sample(p.x > 10 ? child1 : child2); }",
|
|
|
|
"expression");
|
|
|
|
}
|
2020-08-12 15:36:56 +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-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "half4 main() { return sk_Caps.integerSupport ? half4(1) : half4(0); }",
|
|
|
|
"unknown identifier 'sk_Caps'");
|
|
|
|
}
|
2020-11-06 16:45:36 +00:00
|
|
|
|
2021-01-12 23:39:02 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalid_LateErrors, r) {
|
2020-08-12 15:36:56 +00:00
|
|
|
// Errors that aren't caught until later in the compilation process (during optimize())
|
2021-01-12 23:39:02 +00:00
|
|
|
test_invalid_effect(r, "half4 main() { return half4(1); return half4(0); }", "unreachable");
|
|
|
|
test_invalid_effect(r, "half4 badFunc() {}"
|
|
|
|
"half4 main() { return badFunc(); }",
|
|
|
|
"without returning");
|
2019-12-19 20:44:56 +00:00
|
|
|
}
|
2020-01-08 18:19:58 +00:00
|
|
|
|
2020-08-05 20:48:58 +00:00
|
|
|
DEF_TEST(SkRuntimeEffectInvalidColorFilters, r) {
|
|
|
|
auto test = [r](const char* sksl) {
|
|
|
|
auto [effect, errorText] = SkRuntimeEffect::Make(SkString(sksl));
|
|
|
|
REPORTER_ASSERT(r, effect);
|
2020-08-06 16:15:53 +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
|
|
|
sk_sp<SkData> uniforms = SkData::MakeUninitialized(effect->uniformSize());
|
2020-08-06 16:15:53 +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
|
|
|
REPORTER_ASSERT(r, effect->makeShader(uniforms, nullptr, 0, nullptr, false));
|
|
|
|
REPORTER_ASSERT(r, !effect->makeColorFilter(uniforms));
|
2020-08-05 20:48:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Runtime effects that use sample coords or sk_FragCoord are valid shaders,
|
|
|
|
// but not valid color filters
|
2020-08-13 20:59:48 +00:00
|
|
|
test("half4 main(float2 p) { return half2(p).xy01; }");
|
|
|
|
test("half4 main(float2 p) { return half2(sk_FragCoord.xy).xy01; }");
|
2020-08-06 16:15:53 +00:00
|
|
|
|
|
|
|
// We also can't use layout(marker), which would give the runtime color filter CTM information
|
|
|
|
test("layout(marker=ctm) uniform float4x4 ctm;"
|
2020-08-13 20:59:48 +00:00
|
|
|
"half4 main(float2 p) { return half4(half(ctm[0][0]), 0, 0, 1); }");
|
2020-08-05 20:48:58 +00:00
|
|
|
}
|
|
|
|
|
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-02-10 00:05:01 +00:00
|
|
|
auto[effect, errorText] = SkRuntimeEffect::Make(SkString(src));
|
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
|
|
|
}
|
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
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
|
|
|
|
|
|
|
|
void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR,
|
|
|
|
PreTestFn preTestCallback = nullptr) {
|
2020-04-15 18:18:13 +00:00
|
|
|
auto shader = fBuilder->makeShader(nullptr, 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);
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
canvas->save();
|
|
|
|
if (preTestCallback) {
|
|
|
|
preTestCallback(canvas, &paint);
|
|
|
|
}
|
|
|
|
canvas->drawPaint(paint);
|
|
|
|
canvas->restore();
|
|
|
|
|
|
|
|
GrColor actual[4];
|
|
|
|
SkImageInfo info = fSurface->imageInfo();
|
|
|
|
if (!fSurface->readPixels(info, actual, info.minRowBytes(), 0, 0)) {
|
|
|
|
REPORT_FAILURE(fReporter, "readPixels", SkString("readPixels failed"));
|
2020-01-08 18:19:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
GrColor expected[4] = {TL, TR, BL, BR};
|
2020-08-16 03:22:53 +00:00
|
|
|
if (0 != memcmp(actual, expected, sizeof(actual))) {
|
2020-07-22 14:19:02 +00:00
|
|
|
REPORT_FAILURE(fReporter, "Runtime effect didn't match expectations",
|
2020-01-08 18:19:58 +00:00
|
|
|
SkStringPrintf("\n"
|
|
|
|
"Expected: [ %08x %08x %08x %08x ]\n"
|
|
|
|
"Got : [ %08x %08x %08x %08x ]\n"
|
|
|
|
"SkSL:\n%s\n",
|
|
|
|
TL, TR, BL, BR, actual[0], actual[1], actual[2],
|
2020-08-27 14:51:22 +00:00
|
|
|
actual[3], fBuilder->effect()->source().c_str()));
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-22 14:19:02 +00:00
|
|
|
void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
|
|
|
|
this->test(expected, expected, expected, expected, preTestCallback);
|
2020-01-08 18:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-07-22 14:19:02 +00:00
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
sk_sp<SkSurface> fSurface;
|
2020-04-15 18:18:13 +00:00
|
|
|
SkTLazy<SkRuntimeShaderBuilder> 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>;
|
|
|
|
|
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); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
|
|
|
|
// Use of a simple uniform. (Draw twice with two values to ensure it's updated).
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform float4 gColor; half4 main() { 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
|
|
|
|
|
|
|
|
// 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.
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("half4 main() { return half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.test(0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F,
|
|
|
|
[](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); }");
|
2020-09-02 17:45:47 +00:00
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
|
2020-10-13 13:34:23 +00:00
|
|
|
// ... and support GLSL type names
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("half4 main(float2 p) { return vec4(p - 0.5, 0, 1); }");
|
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
|
|
|
|
// ... and support *returning* float4 (aka vec4), not just half4
|
|
|
|
effect.build("float4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
|
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
effect.build("vec4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
|
2020-10-13 13:34:23 +00:00
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
|
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); }");
|
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
effect.build("void moveCoords(inout vec2 p) { p -= 0.5; }"
|
|
|
|
"vec4 main(vec2 p) { moveCoords(p); return vec4(p, 0, 1); }");
|
|
|
|
effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
|
|
|
|
|
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;"
|
|
|
|
"half4 main() { return sample(child); }");
|
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();
|
|
|
|
|
|
|
|
// Sampling a simple child at our coordinates (implicitly)
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
|
|
|
"half4 main() { return sample(child); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
|
|
|
effect.test(0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF);
|
|
|
|
|
|
|
|
// Sampling with explicit coordinates (reflecting about the diagonal)
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
|
|
|
"half4 main(float2 p) { return sample(child, p.yx); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
|
|
|
effect.test(0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF);
|
|
|
|
|
|
|
|
// Sampling with a matrix (again, reflecting about the diagonal)
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("uniform shader child;"
|
|
|
|
"half4 main() { return sample(child, float3x3(0, 1, 0, 1, 0, 0, 0, 0, 1)); }");
|
2020-07-22 14:19:02 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
|
|
|
effect.test(0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF);
|
2020-07-23 17:28:14 +00:00
|
|
|
|
2020-11-04 20:40:50 +00:00
|
|
|
// Legacy behavior - shaders can be declared 'in' rather than 'uniform'
|
2020-11-06 15:42:51 +00:00
|
|
|
effect.build("in shader child;"
|
|
|
|
"half4 main() { return sample(child); }");
|
2020-11-04 20:40:50 +00:00
|
|
|
effect.child("child") = rgbwShader;
|
|
|
|
effect.test(0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF);
|
|
|
|
|
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
|
|
|
|
|
|
|
DEF_TEST(SkRuntimeShaderBuilderReuse, r) {
|
|
|
|
const char* kSource = R"(
|
|
|
|
uniform half x;
|
|
|
|
half4 main() { return half4(x); }
|
|
|
|
)";
|
|
|
|
|
2021-02-10 00:05:01 +00:00
|
|
|
sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(kSource)));
|
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;
|
|
|
|
auto shader_0 = b.makeShader(nullptr, false);
|
|
|
|
|
|
|
|
b.uniform("x") = 1.0f;
|
|
|
|
auto shader_1 = b.makeShader(nullptr, true);
|
|
|
|
}
|
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;
|
|
|
|
half4 main() { return half4(x); }
|
|
|
|
)";
|
|
|
|
|
2021-02-10 00:05:01 +00:00
|
|
|
sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(kSource)));
|
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
|
|
|
|
|
|
|
|
|
|
|
|
auto shader = b.makeShader(nullptr, false);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
static constexpr char kSource[] = "half4 main() { return sk_FragCoord.xyxy; }";
|
|
|
|
|
|
|
|
std::thread threads[16];
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
thread = std::thread([r]() {
|
|
|
|
auto [effect, error] = SkRuntimeEffect::Make(SkString(kSource));
|
|
|
|
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().
|
|
|
|
auto [effect, err] = SkRuntimeEffect::Make(SkString{
|
|
|
|
"uniform shader input; half4 main() { half4 c = sample(input); return c*c; }"});
|
|
|
|
REPORTER_ASSERT(r, effect);
|
|
|
|
REPORTER_ASSERT(r, err.isEmpty());
|
|
|
|
|
|
|
|
sk_sp<SkColorFilter> input = nullptr;
|
|
|
|
sk_sp<SkColorFilter> cf = effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
|
|
|
|
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);
|
|
|
|
}
|