• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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