blob: b295e93c2448f3c258d69879141b0de909f98783 [file] [log] [blame]
Brian Osman088913a2019-12-19 15:44:56 -05001/*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Brian Osman62419612020-07-22 10:19:02 -04008#include "include/core/SkBitmap.h"
Brian Osmanf72dedd2020-01-08 13:19:58 -05009#include "include/core/SkCanvas.h"
Brian Osman92aac1e2020-08-05 16:48:58 -040010#include "include/core/SkColorFilter.h"
Brian Osman269b21c2020-08-06 12:15:53 -040011#include "include/core/SkData.h"
Brian Osmanf72dedd2020-01-08 13:19:58 -050012#include "include/core/SkPaint.h"
13#include "include/core/SkSurface.h"
Brian Osmanee426f22020-01-02 11:55:24 -050014#include "include/effects/SkRuntimeEffect.h"
Robert Phillips6d344c32020-07-06 10:56:46 -040015#include "include/gpu/GrDirectContext.h"
Brian Salomon5392c942021-03-30 16:14:37 -040016#include "src/core/SkColorSpacePriv.h"
Brian Osmand9bde072020-04-15 14:18:13 -040017#include "src/core/SkTLazy.h"
Brian Osman62419612020-07-22 10:19:02 -040018#include "src/gpu/GrColor.h"
Brian Osman088913a2019-12-19 15:44:56 -050019#include "tests/Test.h"
20
Brian Osmanf72dedd2020-01-08 13:19:58 -050021#include <algorithm>
Brian Osman8e2ef022020-09-30 13:26:43 -040022#include <thread>
Brian Osmanf72dedd2020-01-08 13:19:58 -050023
John Stiles7ce17512021-01-12 18:39:02 -050024void test_invalid_effect(skiatest::Reporter* r, const char* src, const char* expected) {
25 auto [effect, errorText] = SkRuntimeEffect::Make(SkString(src));
26 REPORTER_ASSERT(r, !effect);
27 REPORTER_ASSERT(r, errorText.contains(expected),
28 "Expected error message to contain \"%s\". Actual message: \"%s\"",
29 expected, errorText.c_str());
30};
Brian Osman088913a2019-12-19 15:44:56 -050031
Brian Osman24c18522020-11-10 16:36:01 -050032#define EMPTY_MAIN "half4 main() { return half4(0); }"
33
John Stiles7ce17512021-01-12 18:39:02 -050034DEF_TEST(SkRuntimeEffectInvalid_FPOnly, r) {
Brian Osman088913a2019-12-19 15:44:56 -050035 // Features that are only allowed in .fp files (key, in uniform, ctype, when, tracked).
36 // Ensure that these fail, and the error messages contain the relevant keyword.
John Stiles7ce17512021-01-12 18:39:02 -050037 test_invalid_effect(r, "layout(key) in bool Input;" EMPTY_MAIN, "key");
38 test_invalid_effect(r, "in uniform float Input;" EMPTY_MAIN, "in uniform");
39 test_invalid_effect(r, "layout(ctype=SkRect) float4 Input;" EMPTY_MAIN, "ctype");
40 test_invalid_effect(r, "in bool Flag; "
41 "layout(when=Flag) uniform float Input;" EMPTY_MAIN, "when");
42 test_invalid_effect(r, "layout(tracked) uniform float Input;" EMPTY_MAIN, "tracked");
43}
Brian Osman088913a2019-12-19 15:44:56 -050044
John Stiles7ce17512021-01-12 18:39:02 -050045DEF_TEST(SkRuntimeEffectInvalid_LimitedUniformTypes, r) {
Brian Osmanb06301e2020-11-06 11:45:36 -050046 // Runtime SkSL supports a limited set of uniform types. No bool, or int, for example:
John Stiles7ce17512021-01-12 18:39:02 -050047 test_invalid_effect(r, "uniform bool b;" EMPTY_MAIN, "uniform");
48 test_invalid_effect(r, "uniform int i;" EMPTY_MAIN, "uniform");
49}
Brian Osman088913a2019-12-19 15:44:56 -050050
John Stiles7ce17512021-01-12 18:39:02 -050051DEF_TEST(SkRuntimeEffectInvalid_NoInVariables, r) {
Brian Osmana4b91692020-08-10 14:26:16 -040052 // 'in' variables aren't allowed at all:
John Stiles7ce17512021-01-12 18:39:02 -050053 test_invalid_effect(r, "in bool b;" EMPTY_MAIN, "'in'");
54 test_invalid_effect(r, "in float f;" EMPTY_MAIN, "'in'");
55 test_invalid_effect(r, "in float2 v;" EMPTY_MAIN, "'in'");
56 test_invalid_effect(r, "in half3x3 m;" EMPTY_MAIN, "'in'");
57}
Brian Osman8783b782020-01-06 11:13:45 -050058
John Stiles7ce17512021-01-12 18:39:02 -050059DEF_TEST(SkRuntimeEffectInvalid_MarkerRequiresFloat4x4, r) {
Brian Osmana4b91692020-08-10 14:26:16 -040060 // 'marker' is only permitted on float4x4 uniforms
John Stiles7ce17512021-01-12 18:39:02 -050061 test_invalid_effect(r,
62 "layout(marker=local_to_world) uniform float3x3 localToWorld;" EMPTY_MAIN,
63 "float4x4");
64}
Brian Osmane64ae862020-07-16 15:29:15 -040065
John Stiles7ce17512021-01-12 18:39:02 -050066DEF_TEST(SkRuntimeEffectInvalid_UndefinedFunction, r) {
67 test_invalid_effect(r, "half4 missing(); half4 main() { return missing(); }",
68 "undefined function");
69}
Brian Osman182c92e2020-07-20 15:18:33 -040070
John Stiles7ce17512021-01-12 18:39:02 -050071DEF_TEST(SkRuntimeEffectInvalid_UndefinedMain, r) {
Brian Osman182c92e2020-07-20 15:18:33 -040072 // Shouldn't be possible to create an SkRuntimeEffect without "main"
John Stiles7ce17512021-01-12 18:39:02 -050073 test_invalid_effect(r, "", "main");
74}
Brian Osman82329002020-07-21 09:39:27 -040075
John Stiles7ce17512021-01-12 18:39:02 -050076DEF_TEST(SkRuntimeEffectInvalid_ShaderLimitations, r) {
Brian Osman82329002020-07-21 09:39:27 -040077 // Various places that shaders (fragmentProcessors) should not be allowed
John Stiles7ce17512021-01-12 18:39:02 -050078 test_invalid_effect(r, "half4 main() { shader child; return sample(child); }",
79 "must be global");
80 test_invalid_effect(r, "uniform shader child; half4 helper(shader fp) { return sample(fp); }"
81 "half4 main() { return helper(child); }",
82 "parameter");
83 test_invalid_effect(r, "uniform shader child; shader get_child() { return child; }"
84 "half4 main() { return sample(get_child()); }",
85 "return");
86 test_invalid_effect(r, "uniform shader child;"
87 "half4 main() { return sample(shader(child)); }",
88 "construct");
89 test_invalid_effect(r, "uniform shader child1; uniform shader child2;"
90 "half4 main(float2 p) { return sample(p.x > 10 ? child1 : child2); }",
91 "expression");
92}
Brian Osmane12939f2020-08-12 11:36:56 -040093
John Stiles7ce17512021-01-12 18:39:02 -050094DEF_TEST(SkRuntimeEffectInvalid_SkCapsDisallowed, r) {
Brian Osmanb06301e2020-11-06 11:45:36 -050095 // sk_Caps is an internal system. It should not be visible to runtime effects
John Stiles7ce17512021-01-12 18:39:02 -050096 test_invalid_effect(r, "half4 main() { return sk_Caps.integerSupport ? half4(1) : half4(0); }",
97 "unknown identifier 'sk_Caps'");
98}
Brian Osmanb06301e2020-11-06 11:45:36 -050099
Brian Osman92aac1e2020-08-05 16:48:58 -0400100DEF_TEST(SkRuntimeEffectInvalidColorFilters, r) {
101 auto test = [r](const char* sksl) {
102 auto [effect, errorText] = SkRuntimeEffect::Make(SkString(sksl));
103 REPORTER_ASSERT(r, effect);
Brian Osman269b21c2020-08-06 12:15:53 -0400104
Brian Osmana4b91692020-08-10 14:26:16 -0400105 sk_sp<SkData> uniforms = SkData::MakeUninitialized(effect->uniformSize());
Brian Osman269b21c2020-08-06 12:15:53 -0400106
Brian Osmana4b91692020-08-10 14:26:16 -0400107 REPORTER_ASSERT(r, effect->makeShader(uniforms, nullptr, 0, nullptr, false));
108 REPORTER_ASSERT(r, !effect->makeColorFilter(uniforms));
Brian Osman92aac1e2020-08-05 16:48:58 -0400109 };
110
111 // Runtime effects that use sample coords or sk_FragCoord are valid shaders,
112 // but not valid color filters
Brian Osman767f4442020-08-13 16:59:48 -0400113 test("half4 main(float2 p) { return half2(p).xy01; }");
114 test("half4 main(float2 p) { return half2(sk_FragCoord.xy).xy01; }");
Brian Osman269b21c2020-08-06 12:15:53 -0400115
116 // We also can't use layout(marker), which would give the runtime color filter CTM information
117 test("layout(marker=ctm) uniform float4x4 ctm;"
Brian Osman767f4442020-08-13 16:59:48 -0400118 "half4 main(float2 p) { return half4(half(ctm[0][0]), 0, 0, 1); }");
Brian Osman92aac1e2020-08-05 16:48:58 -0400119}
120
Brian Osmanf72dedd2020-01-08 13:19:58 -0500121class TestEffect {
122public:
Brian Osman62419612020-07-22 10:19:02 -0400123 TestEffect(skiatest::Reporter* r, sk_sp<SkSurface> surface)
124 : fReporter(r), fSurface(std::move(surface)) {}
125
Brian Osman33316412020-11-06 10:42:51 -0500126 void build(const char* src) {
John Stiles1f19ce22021-02-11 17:40:03 -0500127 auto [effect, errorText] = SkRuntimeEffect::Make(SkString(src));
Brian Osmanf72dedd2020-01-08 13:19:58 -0500128 if (!effect) {
Brian Osman62419612020-07-22 10:19:02 -0400129 REPORT_FAILURE(fReporter, "effect",
Brian Osmanf72dedd2020-01-08 13:19:58 -0500130 SkStringPrintf("Effect didn't compile: %s", errorText.c_str()));
131 return;
132 }
Brian Osmand9bde072020-04-15 14:18:13 -0400133 fBuilder.init(std::move(effect));
Brian Osmanf72dedd2020-01-08 13:19:58 -0500134 }
135
Brian Osmana4b91692020-08-10 14:26:16 -0400136 SkRuntimeShaderBuilder::BuilderUniform uniform(const char* name) {
137 return fBuilder->uniform(name);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500138 }
Brian Osman62419612020-07-22 10:19:02 -0400139 SkRuntimeShaderBuilder::BuilderChild child(const char* name) {
140 return fBuilder->child(name);
141 }
Brian Osmanf72dedd2020-01-08 13:19:58 -0500142
Brian Osman62419612020-07-22 10:19:02 -0400143 using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
144
145 void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR,
146 PreTestFn preTestCallback = nullptr) {
Brian Osmand9bde072020-04-15 14:18:13 -0400147 auto shader = fBuilder->makeShader(nullptr, false);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500148 if (!shader) {
Brian Osman62419612020-07-22 10:19:02 -0400149 REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader"));
Brian Osmanf72dedd2020-01-08 13:19:58 -0500150 return;
151 }
152
Brian Osman62419612020-07-22 10:19:02 -0400153 SkCanvas* canvas = fSurface->getCanvas();
Brian Osmanf72dedd2020-01-08 13:19:58 -0500154 SkPaint paint;
155 paint.setShader(std::move(shader));
156 paint.setBlendMode(SkBlendMode::kSrc);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500157
Brian Osman62419612020-07-22 10:19:02 -0400158 canvas->save();
159 if (preTestCallback) {
160 preTestCallback(canvas, &paint);
161 }
162 canvas->drawPaint(paint);
163 canvas->restore();
164
165 GrColor actual[4];
166 SkImageInfo info = fSurface->imageInfo();
167 if (!fSurface->readPixels(info, actual, info.minRowBytes(), 0, 0)) {
168 REPORT_FAILURE(fReporter, "readPixels", SkString("readPixels failed"));
Brian Osmanf72dedd2020-01-08 13:19:58 -0500169 return;
170 }
171
Brian Osman62419612020-07-22 10:19:02 -0400172 GrColor expected[4] = {TL, TR, BL, BR};
John Stilesc1c3c6d2020-08-15 23:22:53 -0400173 if (0 != memcmp(actual, expected, sizeof(actual))) {
Brian Osman62419612020-07-22 10:19:02 -0400174 REPORT_FAILURE(fReporter, "Runtime effect didn't match expectations",
Brian Osmanf72dedd2020-01-08 13:19:58 -0500175 SkStringPrintf("\n"
176 "Expected: [ %08x %08x %08x %08x ]\n"
177 "Got : [ %08x %08x %08x %08x ]\n"
178 "SkSL:\n%s\n",
179 TL, TR, BL, BR, actual[0], actual[1], actual[2],
Brian Osmanb6bd0d22020-08-27 10:51:22 -0400180 actual[3], fBuilder->effect()->source().c_str()));
Brian Osmanf72dedd2020-01-08 13:19:58 -0500181 }
182 }
183
Brian Osman62419612020-07-22 10:19:02 -0400184 void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
185 this->test(expected, expected, expected, expected, preTestCallback);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500186 }
187
188private:
Brian Osman62419612020-07-22 10:19:02 -0400189 skiatest::Reporter* fReporter;
190 sk_sp<SkSurface> fSurface;
Brian Osmand9bde072020-04-15 14:18:13 -0400191 SkTLazy<SkRuntimeShaderBuilder> fBuilder;
Brian Osmanf72dedd2020-01-08 13:19:58 -0500192};
193
Brian Osman62419612020-07-22 10:19:02 -0400194// Produces a 2x2 bitmap shader, with opaque colors:
195// [ Red, Green ]
196// [ Blue, White ]
197static sk_sp<SkShader> make_RGBW_shader() {
198 SkBitmap bmp;
199 bmp.allocPixels(SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
200 SkIRect topLeft = SkIRect::MakeWH(1, 1);
201 bmp.pixmap().erase(SK_ColorRED, topLeft);
202 bmp.pixmap().erase(SK_ColorGREEN, topLeft.makeOffset(1, 0));
203 bmp.pixmap().erase(SK_ColorBLUE, topLeft.makeOffset(0, 1));
204 bmp.pixmap().erase(SK_ColorWHITE, topLeft.makeOffset(1, 1));
Mike Reedb41bd152020-12-12 11:18:31 -0500205 return bmp.makeShader(SkSamplingOptions());
Brian Osman62419612020-07-22 10:19:02 -0400206}
207
Robert Phillipse94b4e12020-07-23 13:54:35 -0400208static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrRecordingContext* rContext) {
Brian Osmanf72dedd2020-01-08 13:19:58 -0500209 SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Robert Phillipse94b4e12020-07-23 13:54:35 -0400210 sk_sp<SkSurface> surface = rContext
211 ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
212 : SkSurface::MakeRaster(info);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500213 REPORTER_ASSERT(r, surface);
Brian Osman62419612020-07-22 10:19:02 -0400214 TestEffect effect(r, surface);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500215
Brian Osman504032e2020-01-10 10:05:24 -0500216 using float4 = std::array<float, 4>;
217
Brian Osman62419612020-07-22 10:19:02 -0400218 // Local coords
Brian Osman33316412020-11-06 10:42:51 -0500219 effect.build("half4 main(float2 p) { return half4(half2(p - 0.5), 0, 1); }");
Brian Osman62419612020-07-22 10:19:02 -0400220 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500221
Brian Osman62419612020-07-22 10:19:02 -0400222 // Use of a simple uniform. (Draw twice with two values to ensure it's updated).
Brian Osman33316412020-11-06 10:42:51 -0500223 effect.build("uniform float4 gColor; half4 main() { return half4(gColor); }");
Brian Osmana4b91692020-08-10 14:26:16 -0400224 effect.uniform("gColor") = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
Brian Osman62419612020-07-22 10:19:02 -0400225 effect.test(0xFFBF4000);
Brian Osmana4b91692020-08-10 14:26:16 -0400226 effect.uniform("gColor") = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
Brian Osman62419612020-07-22 10:19:02 -0400227 effect.test(0x7F00007F); // Tests that we clamp to valid premul
Michael Ludwig5e6b3cd2020-05-27 17:02:37 -0400228
Brian Osman62419612020-07-22 10:19:02 -0400229 // Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords.
230 // Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to
231 // make sure we're not saturating unexpectedly.
Brian Osman33316412020-11-06 10:42:51 -0500232 effect.build("half4 main() { return half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1); }");
Brian Osman62419612020-07-22 10:19:02 -0400233 effect.test(0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F,
234 [](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); });
Michael Ludwig22534f22020-05-27 17:25:33 -0400235
Brian Osman0acb5b52020-09-02 13:45:47 -0400236 // Runtime effects should use relaxed precision rules by default
Brian Osman33316412020-11-06 10:42:51 -0500237 effect.build("half4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
Brian Osman0acb5b52020-09-02 13:45:47 -0400238 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
239
Brian Osmanf1319c32020-10-13 09:34:23 -0400240 // ... and support GLSL type names
Brian Osman33316412020-11-06 10:42:51 -0500241 effect.build("half4 main(float2 p) { return vec4(p - 0.5, 0, 1); }");
242 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
243
244 // ... and support *returning* float4 (aka vec4), not just half4
245 effect.build("float4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
246 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
247 effect.build("vec4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
Brian Osmanf1319c32020-10-13 09:34:23 -0400248 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
249
Brian Osmanb4ce9442020-11-11 09:18:02 -0500250 // Mutating coords should work. (skbug.com/10918)
251 effect.build("vec4 main(vec2 p) { p -= 0.5; return vec4(p, 0, 1); }");
252 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
253 effect.build("void moveCoords(inout vec2 p) { p -= 0.5; }"
254 "vec4 main(vec2 p) { moveCoords(p); return vec4(p, 0, 1); }");
255 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
256
Brian Osmanb5f0f522020-07-23 13:28:14 -0400257 //
258 // Sampling children
259 //
260
Brian Osman62419612020-07-22 10:19:02 -0400261 // Sampling a null child should return the paint color
Brian Osman33316412020-11-06 10:42:51 -0500262 effect.build("uniform shader child;"
263 "half4 main() { return sample(child); }");
Brian Osman62419612020-07-22 10:19:02 -0400264 effect.child("child") = nullptr;
265 effect.test(0xFF00FFFF,
266 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
267
268 sk_sp<SkShader> rgbwShader = make_RGBW_shader();
269
270 // Sampling a simple child at our coordinates (implicitly)
Brian Osman33316412020-11-06 10:42:51 -0500271 effect.build("uniform shader child;"
272 "half4 main() { return sample(child); }");
Brian Osman62419612020-07-22 10:19:02 -0400273 effect.child("child") = rgbwShader;
274 effect.test(0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF);
275
276 // Sampling with explicit coordinates (reflecting about the diagonal)
Brian Osman33316412020-11-06 10:42:51 -0500277 effect.build("uniform shader child;"
278 "half4 main(float2 p) { return sample(child, p.yx); }");
Brian Osman62419612020-07-22 10:19:02 -0400279 effect.child("child") = rgbwShader;
280 effect.test(0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF);
281
282 // Sampling with a matrix (again, reflecting about the diagonal)
Brian Osman33316412020-11-06 10:42:51 -0500283 effect.build("uniform shader child;"
284 "half4 main() { return sample(child, float3x3(0, 1, 0, 1, 0, 0, 0, 0, 1)); }");
Brian Osman62419612020-07-22 10:19:02 -0400285 effect.child("child") = rgbwShader;
286 effect.test(0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF);
Brian Osmanb5f0f522020-07-23 13:28:14 -0400287
Brian Osman91292e92020-11-04 15:40:50 -0500288 // Legacy behavior - shaders can be declared 'in' rather than 'uniform'
Brian Osman33316412020-11-06 10:42:51 -0500289 effect.build("in shader child;"
290 "half4 main() { return sample(child); }");
Brian Osman91292e92020-11-04 15:40:50 -0500291 effect.child("child") = rgbwShader;
292 effect.test(0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF);
293
Brian Osmanb5f0f522020-07-23 13:28:14 -0400294 //
295 // Helper functions
296 //
297
298 // Test case for inlining in the pipeline-stage and fragment-shader passes (skbug.com/10526):
Brian Osman33316412020-11-06 10:42:51 -0500299 effect.build("float2 helper(float2 x) { return x + 1; }"
300 "half4 main(float2 p) { float2 v = helper(p); return half4(half2(v), 0, 1); }");
Brian Osmanb5f0f522020-07-23 13:28:14 -0400301 effect.test(0xFF00FFFF);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500302}
303
304DEF_TEST(SkRuntimeEffectSimple, r) {
305 test_RuntimeEffect_Shaders(r, nullptr);
306}
307
308DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectSimple_GPU, r, ctxInfo) {
Robert Phillips6d344c32020-07-06 10:56:46 -0400309 test_RuntimeEffect_Shaders(r, ctxInfo.directContext());
Brian Osmanf72dedd2020-01-08 13:19:58 -0500310}
Brian Osmanb6bd0d22020-08-27 10:51:22 -0400311
312DEF_TEST(SkRuntimeShaderBuilderReuse, r) {
313 const char* kSource = R"(
314 uniform half x;
315 half4 main() { return half4(x); }
316 )";
317
John Stiles1f19ce22021-02-11 17:40:03 -0500318 sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::Make(SkString(kSource)).effect;
Brian Osmanb6bd0d22020-08-27 10:51:22 -0400319 REPORTER_ASSERT(r, effect);
320
321 // Test passes if this sequence doesn't assert. skbug.com/10667
322 SkRuntimeShaderBuilder b(std::move(effect));
323 b.uniform("x") = 0.0f;
324 auto shader_0 = b.makeShader(nullptr, false);
325
326 b.uniform("x") = 1.0f;
327 auto shader_1 = b.makeShader(nullptr, true);
328}
Brian Osman8e2ef022020-09-30 13:26:43 -0400329
Derek Sollenberger9e1cedd2021-01-14 08:30:52 -0500330DEF_TEST(SkRuntimeShaderBuilderSetUniforms, r) {
331 const char* kSource = R"(
332 uniform half x;
333 uniform vec2 offset;
334 half4 main() { return half4(x); }
335 )";
336
John Stiles1f19ce22021-02-11 17:40:03 -0500337 sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::Make(SkString(kSource)).effect;
Derek Sollenberger9e1cedd2021-01-14 08:30:52 -0500338 REPORTER_ASSERT(r, effect);
339
340 SkRuntimeShaderBuilder b(std::move(effect));
341
342 // Test passes if this sequence doesn't assert.
343 float x = 1.0f;
344 REPORTER_ASSERT(r, b.uniform("x").set(&x, 1));
345
346 // add extra value to ensure that set doesn't try to use sizeof(array)
347 float origin[] = { 2.0f, 3.0f, 4.0f };
348 REPORTER_ASSERT(r, b.uniform("offset").set<float>(origin, 2));
349
350#ifndef SK_DEBUG
351 REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 1));
352 REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 3));
353#endif
354
355
356 auto shader = b.makeShader(nullptr, false);
357}
358
Brian Osman8e2ef022020-09-30 13:26:43 -0400359DEF_TEST(SkRuntimeEffectThreaded, r) {
360 // SkRuntimeEffect uses a single compiler instance, but it's mutex locked.
361 // This tests that we can safely use it from more than one thread, and also
362 // that programs don't refer to shared structures owned by the compiler.
363 // skbug.com/10589
364 static constexpr char kSource[] = "half4 main() { return sk_FragCoord.xyxy; }";
365
366 std::thread threads[16];
367 for (auto& thread : threads) {
368 thread = std::thread([r]() {
369 auto [effect, error] = SkRuntimeEffect::Make(SkString(kSource));
370 REPORTER_ASSERT(r, effect);
371 });
372 }
373
374 for (auto& thread : threads) {
375 thread.join();
376 }
377}
Mike Klein827f8c02021-02-06 09:13:01 -0600378
379DEF_TEST(SkRuntimeColorFilterSingleColor, r) {
380 // Test runtime colorfilters support filterColor4f().
381 auto [effect, err] = SkRuntimeEffect::Make(SkString{
382 "uniform shader input; half4 main() { half4 c = sample(input); return c*c; }"});
383 REPORTER_ASSERT(r, effect);
384 REPORTER_ASSERT(r, err.isEmpty());
385
386 sk_sp<SkColorFilter> input = nullptr;
387 sk_sp<SkColorFilter> cf = effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
388 REPORTER_ASSERT(r, cf);
389
390 SkColor4f c = cf->filterColor4f({0.25, 0.5, 0.75, 1.0},
391 sk_srgb_singleton(), sk_srgb_singleton());
392 REPORTER_ASSERT(r, c.fR == 0.0625f);
393 REPORTER_ASSERT(r, c.fG == 0.25f);
394 REPORTER_ASSERT(r, c.fB == 0.5625f);
395 REPORTER_ASSERT(r, c.fA == 1.0f);
396}
Brian Osman8e756f32021-02-10 10:19:27 -0500397
398static void test_RuntimeEffectStructNameReuse(skiatest::Reporter* r, GrRecordingContext* rContext) {
399 // Test that two different runtime effects can reuse struct names in a single paint operation
400 auto [childEffect, err] = SkRuntimeEffect::Make(SkString(
401 "uniform shader paint;"
402 "struct S { half4 rgba; };"
403 "void process(inout S s) { s.rgba.rgb *= 0.5; }"
404 "half4 main() { S s; s.rgba = sample(paint); process(s); return s.rgba; }"
405 ));
406 REPORTER_ASSERT(r, childEffect, "%s\n", err.c_str());
407 sk_sp<SkShader> nullChild = nullptr;
408 sk_sp<SkShader> child = childEffect->makeShader(/*uniforms=*/nullptr, &nullChild,
409 /*childCount=*/1, /*localMatrix=*/nullptr,
410 /*isOpaque=*/false);
411
412 SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
413 sk_sp<SkSurface> surface = rContext
414 ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
415 : SkSurface::MakeRaster(info);
416 REPORTER_ASSERT(r, surface);
417
418 TestEffect effect(r, surface);
419 effect.build(
420 "uniform shader child;"
421 "struct S { float2 coord; };"
422 "void process(inout S s) { s.coord = s.coord.yx; }"
423 "half4 main(float2 p) { S s; s.coord = p; process(s); return sample(child, s.coord); "
424 "}");
425 effect.child("child") = child;
426 effect.test(0xFF00407F, [](SkCanvas*, SkPaint* paint) {
427 paint->setColor4f({0.99608f, 0.50196f, 0.0f, 1.0f});
428 });
429}
430
431DEF_TEST(SkRuntimeStructNameReuse, r) {
432 test_RuntimeEffectStructNameReuse(r, nullptr);
433}
434
435DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeStructNameReuse_GPU, r, ctxInfo) {
436 test_RuntimeEffectStructNameReuse(r, ctxInfo.directContext());
437}
Mike Kleine0d9b862021-02-16 12:00:29 -0600438
439DEF_TEST(SkRuntimeColorFilterFlags, r) {
440 { // Here's a non-trivial filter that doesn't change alpha.
441 auto [effect, err] = SkRuntimeEffect::Make(SkString{
442 "uniform shader input; half4 main() { return sample(input) + half4(1,1,1,0); }"});
443 REPORTER_ASSERT(r, effect && err.isEmpty());
444 sk_sp<SkColorFilter> input = nullptr,
445 filter = effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
446 REPORTER_ASSERT(r, filter && filter->isAlphaUnchanged());
447 }
448
449 { // Here's one that definitely changes alpha.
450 auto [effect, err] = SkRuntimeEffect::Make(SkString{
451 "uniform shader input; half4 main() { return sample(input) + half4(0,0,0,4); }"});
452 REPORTER_ASSERT(r, effect && err.isEmpty());
453 sk_sp<SkColorFilter> input = nullptr,
454 filter = effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
455 REPORTER_ASSERT(r, filter && !filter->isAlphaUnchanged());
456 }
457}