1 /*
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
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkSurface.h"
14 #include "include/effects/SkRuntimeEffect.h"
15 #include "include/gpu/GrDirectContext.h"
16 #include "include/sksl/DSLRuntimeEffects.h"
17 #include "src/core/SkRuntimeEffectPriv.h"
18 #include "src/core/SkTLazy.h"
19 #include "src/gpu/GrColor.h"
20 #include "src/sksl/SkSLCompiler.h"
21 #include "tests/Test.h"
22
23 #include <algorithm>
24 #include <thread>
25
26 using namespace SkSL::dsl;
27
28 class DSLTestEffect {
29 public:
DSLTestEffect(skiatest::Reporter * r,sk_sp<SkSurface> surface)30 DSLTestEffect(skiatest::Reporter* r, sk_sp<SkSurface> surface)
31 : fReporter(r)
32 , fCaps(SkSL::ShaderCapsFactory::Standalone())
33 , fCompiler(std::make_unique<SkSL::Compiler>(fCaps.get()))
34 , fSurface(std::move(surface)) {}
35
start()36 void start() {
37 StartRuntimeShader(fCompiler.get());
38 }
39
end(bool expectSuccess=true)40 void end(bool expectSuccess = true) {
41 SkRuntimeEffect::Options options;
42 SkRuntimeEffectPriv::EnableFragCoord(&options);
43 sk_sp<SkRuntimeEffect> effect = EndRuntimeShader(options);
44 REPORTER_ASSERT(fReporter, effect ? expectSuccess : !expectSuccess);
45 if (effect) {
46 fBuilder.init(std::move(effect));
47 }
48 }
49
uniform(skstd::string_view name)50 SkRuntimeShaderBuilder::BuilderUniform uniform(skstd::string_view name) {
51 return fBuilder->uniform(SkString(name).c_str());
52 }
53
child(skstd::string_view name)54 SkRuntimeShaderBuilder::BuilderChild child(skstd::string_view name) {
55 return fBuilder->child(SkString(name).c_str());
56 }
57
58 using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
59
test(GrColor TL,GrColor TR,GrColor BL,GrColor BR,PreTestFn preTestCallback=nullptr)60 void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR,
61 PreTestFn preTestCallback = nullptr) {
62 auto shader = fBuilder->makeShader(nullptr, false);
63 if (!shader) {
64 REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader"));
65 return;
66 }
67
68 SkCanvas* canvas = fSurface->getCanvas();
69 SkPaint paint;
70 paint.setShader(std::move(shader));
71 paint.setBlendMode(SkBlendMode::kSrc);
72
73 canvas->save();
74 if (preTestCallback) {
75 preTestCallback(canvas, &paint);
76 }
77 canvas->drawPaint(paint);
78 canvas->restore();
79
80 GrColor actual[4];
81 SkImageInfo info = fSurface->imageInfo();
82 if (!fSurface->readPixels(info, actual, info.minRowBytes(), 0, 0)) {
83 REPORT_FAILURE(fReporter, "readPixels", SkString("readPixels failed"));
84 return;
85 }
86
87 GrColor expected[4] = {TL, TR, BL, BR};
88 if (0 != memcmp(actual, expected, sizeof(actual))) {
89 REPORT_FAILURE(fReporter, "Runtime effect didn't match expectations",
90 SkStringPrintf("\n"
91 "Expected: [ %08x %08x %08x %08x ]\n"
92 "Got : [ %08x %08x %08x %08x ]\n"
93 "SkSL:\n%s\n",
94 TL, TR, BL, BR, actual[0], actual[1], actual[2],
95 actual[3], fBuilder->effect()->source().c_str()));
96 }
97 }
98
test(GrColor expected,PreTestFn preTestCallback=nullptr)99 void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
100 this->test(expected, expected, expected, expected, preTestCallback);
101 }
102
103 private:
104 skiatest::Reporter* fReporter;
105 SkSL::ShaderCapsPointer fCaps;
106 std::unique_ptr<SkSL::Compiler> fCompiler;
107 sk_sp<SkSurface> fSurface;
108 SkTLazy<SkRuntimeShaderBuilder> fBuilder;
109 };
110
test_RuntimeEffect_Shaders(skiatest::Reporter * r,GrRecordingContext * rContext)111 static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrRecordingContext* rContext) {
112 SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
113 sk_sp<SkSurface> surface = rContext
114 ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
115 : SkSurface::MakeRaster(info);
116 REPORTER_ASSERT(r, surface);
117 using float4 = std::array<float, 4>;
118 using int4 = std::array<int, 4>;
119 DSLTestEffect effect(r, surface);
120
121 // Local coords
122 {
123 effect.start();
124 Parameter p(kFloat2_Type, "p");
125 Function(kHalf4_Type, "main", p).define(
126 Return(Half4(Half2(p - 0.5), 0, 1))
127 );
128 effect.end();
129 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
130 }
131
132 // Use of a simple uniform. (Draw twice with two values to ensure it's updated).
133 {
134 effect.start();
135 GlobalVar gColor(kUniform_Modifier, kFloat4_Type);
136 Declare(gColor);
137 Parameter p(kFloat2_Type, "p");
138 Function(kHalf4_Type, "main", p).define(
139 Return(Half4(gColor))
140 );
141 effect.end();
142 effect.uniform(SkString(gColor.name()).c_str()) = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
143 effect.test(0xFFBF4000);
144 effect.uniform(SkString(gColor.name()).c_str()) = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
145 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
146 }
147
148 // Same, with integer uniforms
149 {
150 effect.start();
151 GlobalVar gColor(kUniform_Modifier, kInt4_Type);
152 Declare(gColor);
153 Parameter p(kFloat2_Type, "p");
154 Function(kHalf4_Type, "main", p).define(
155 Return(Half4(gColor) / 255)
156 );
157 effect.end();
158 effect.uniform(SkString(gColor.name()).c_str()) = int4{ 0x00, 0x40, 0xBF, 0xFF };
159 effect.test(0xFFBF4000);
160 effect.uniform(SkString(gColor.name()).c_str()) = int4{ 0xFF, 0x00, 0x00, 0x7F };
161 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
162 }
163
164 // Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords.
165 // Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to
166 // make sure we're not saturating unexpectedly.
167 {
168 effect.start();
169 Parameter p(kFloat2_Type, "p");
170 Function(kHalf4_Type, "main", p).define(
171 Return(Half4(0.498 * (Half2(Swizzle(sk_FragCoord(), X, Y)) - 0.5), 0, 1))
172 );
173 effect.end();
174 effect.test(0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F,
175 [](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); });
176 }
177
178 // Runtime effects should use relaxed precision rules by default
179 {
180 effect.start();
181 Parameter p(kFloat2_Type, "p");
182 Function(kHalf4_Type, "main", p).define(
183 Return(Float4(p - 0.5, 0, 1))
184 );
185 effect.end();
186 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
187 }
188
189 // ... and support *returning* float4, not just half4
190 {
191 effect.start();
192 Parameter p(kFloat2_Type, "p");
193 Function(kFloat4_Type, "main", p).define(
194 Return(Float4(p - 0.5, 0, 1))
195 );
196 effect.end();
197 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
198 }
199
200 // Test error reporting. We put this before a couple of successful tests to ensure that a
201 // failure doesn't leave us in a broken state.
202 {
203 class SimpleErrorReporter : public SkSL::ErrorReporter {
204 public:
205 void handleError(skstd::string_view msg, SkSL::PositionInfo pos) override {
206 fMsg += msg;
207 }
208
209 SkSL::String fMsg;
210 } errorReporter;
211 effect.start();
212 SetErrorReporter(&errorReporter);
213 Parameter p(kFloat2_Type, "p");
214 Function(kHalf4_Type, "main", p).define(
215 Return(1) // Error, type mismatch
216 );
217 effect.end(false);
218 REPORTER_ASSERT(r, errorReporter.fMsg == "expected 'half4', but found 'int'");
219 }
220
221 // Mutating coords should work. (skbug.com/10918)
222 {
223 effect.start();
224 Parameter p(kFloat2_Type, "p");
225 Function(kFloat4_Type, "main", p).define(
226 p -= 0.5,
227 Return(Float4(p, 0, 1))
228 );
229 effect.end();
230 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
231 }
232 {
233 effect.start();
234 Parameter p1(kInOut_Modifier, kFloat2_Type, "p");
235 Function moveCoords(kVoid_Type, "moveCoords", p1);
236 moveCoords.define(
237 p1 -= 0.5
238 );
239 Parameter p2(kFloat2_Type, "p");
240 Function(kFloat4_Type, "main", p2).define(
241 moveCoords(p2),
242 Return(Float4(p2, 0, 1))
243 );
244 effect.end();
245 effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
246 }
247
248 //
249 // Sampling children
250 //
251
252 // Sampling a null child should return the paint color
253 {
254 effect.start();
255 GlobalVar child(kUniform_Modifier, kShader_Type, "child");
256 Declare(child);
257 Parameter p2(kFloat2_Type, "p");
258 Function(kFloat4_Type, "main", p2).define(
259 Return(child.eval(p2))
260 );
261 effect.end();
262 effect.child(child.name()) = nullptr;
263 effect.test(0xFF00FFFF,
264 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
265 }
266 }
267
DEF_TEST(DSLRuntimeEffectSimple,r)268 DEF_TEST(DSLRuntimeEffectSimple, r) {
269 test_RuntimeEffect_Shaders(r, nullptr);
270 }
271
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DSLRuntimeEffectSimple_GPU,r,ctxInfo)272 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DSLRuntimeEffectSimple_GPU, r, ctxInfo) {
273 test_RuntimeEffect_Shaders(r, ctxInfo.directContext());
274 }
275