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