blob: 8b7f0e95fafc56c0c4aee626a766f013e387fbe3 [file] [log] [blame]
Ethan Nicholas624a5292021-04-16 14:54:43 -04001/*
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"
Brian Osman77046a72021-07-20 13:16:57 -040017#include "src/core/SkRuntimeEffectPriv.h"
Ethan Nicholas624a5292021-04-16 14:54:43 -040018#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
26using namespace SkSL::dsl;
27
28class DSLTestEffect {
29public:
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
36 void start() {
37 StartRuntimeShader(fCompiler.get());
38 }
39
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -040040 void end(bool expectSuccess = true) {
Brian Osman77046a72021-07-20 13:16:57 -040041 SkRuntimeEffect::Options options;
42 SkRuntimeEffectPriv::EnableFragCoord(&options);
43 sk_sp<SkRuntimeEffect> effect = EndRuntimeShader(options);
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -040044 REPORTER_ASSERT(fReporter, effect ? expectSuccess : !expectSuccess);
45 if (effect) {
46 fBuilder.init(std::move(effect));
47 }
Ethan Nicholas624a5292021-04-16 14:54:43 -040048 }
49
Ethan Nicholasd0f4d0d2021-06-23 13:51:55 -040050 SkRuntimeShaderBuilder::BuilderUniform uniform(skstd::string_view name) {
51 return fBuilder->uniform(SkString(name).c_str());
Ethan Nicholas624a5292021-04-16 14:54:43 -040052 }
Ethan Nicholasd0f4d0d2021-06-23 13:51:55 -040053
54 SkRuntimeShaderBuilder::BuilderChild child(skstd::string_view name) {
55 return fBuilder->child(SkString(name).c_str());
Ethan Nicholas624a5292021-04-16 14:54:43 -040056 }
57
58 using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
59
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
99 void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
100 this->test(expected, expected, expected, expected, preTestCallback);
101 }
102
103private:
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
111static 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400124 Parameter p(kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400125 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400135 GlobalVar gColor(kUniform_Modifier, kFloat4_Type);
136 Declare(gColor);
137 Parameter p(kFloat2_Type, "p");
Ethan Nicholas371f6e12021-05-04 14:30:02 -0400138 Function(kHalf4_Type, "main", p).define(
Ethan Nicholas624a5292021-04-16 14:54:43 -0400139 Return(Half4(gColor))
140 );
141 effect.end();
Ethan Nicholasdd2fdea2021-07-20 15:23:04 -0400142 effect.uniform(SkString(gColor.name()).c_str()) = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
Ethan Nicholas624a5292021-04-16 14:54:43 -0400143 effect.test(0xFFBF4000);
Ethan Nicholasdd2fdea2021-07-20 15:23:04 -0400144 effect.uniform(SkString(gColor.name()).c_str()) = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
Brian Osmanfcc0ef12021-09-24 10:51:47 -0400145 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
Ethan Nicholas624a5292021-04-16 14:54:43 -0400146 }
147
148 // Same, with integer uniforms
149 {
150 effect.start();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400151 GlobalVar gColor(kUniform_Modifier, kInt4_Type);
152 Declare(gColor);
153 Parameter p(kFloat2_Type, "p");
Ethan Nicholas371f6e12021-05-04 14:30:02 -0400154 Function(kHalf4_Type, "main", p).define(
Ethan Nicholas624a5292021-04-16 14:54:43 -0400155 Return(Half4(gColor) / 255)
156 );
157 effect.end();
Ethan Nicholasdd2fdea2021-07-20 15:23:04 -0400158 effect.uniform(SkString(gColor.name()).c_str()) = int4{ 0x00, 0x40, 0xBF, 0xFF };
Ethan Nicholas624a5292021-04-16 14:54:43 -0400159 effect.test(0xFFBF4000);
Ethan Nicholasdd2fdea2021-07-20 15:23:04 -0400160 effect.uniform(SkString(gColor.name()).c_str()) = int4{ 0xFF, 0x00, 0x00, 0x7F };
Brian Osmanfcc0ef12021-09-24 10:51:47 -0400161 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
Ethan Nicholas624a5292021-04-16 14:54:43 -0400162 }
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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400169 Parameter p(kFloat2_Type, "p");
Ethan Nicholas371f6e12021-05-04 14:30:02 -0400170 Function(kHalf4_Type, "main", p).define(
Ethan Nicholas624a5292021-04-16 14:54:43 -0400171 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400181 Parameter p(kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400182 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400192 Parameter p(kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400193 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
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400200 // 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 {
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -0400203 class SimpleErrorReporter : public SkSL::ErrorReporter {
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400204 public:
Ethan Nicholas32724122021-09-07 13:49:07 -0400205 void handleError(skstd::string_view msg, SkSL::PositionInfo pos) override {
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -0400206 fMsg += msg;
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400207 }
208
209 SkSL::String fMsg;
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -0400210 } errorReporter;
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400211 effect.start();
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -0400212 SetErrorReporter(&errorReporter);
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400213 Parameter p(kFloat2_Type, "p");
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400214 Function(kHalf4_Type, "main", p).define(
215 Return(1) // Error, type mismatch
216 );
217 effect.end(false);
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -0400218 REPORTER_ASSERT(r, errorReporter.fMsg == "expected 'half4', but found 'int'");
Ethan Nicholas4f3e6a22021-06-15 09:17:05 -0400219 }
220
Ethan Nicholas624a5292021-04-16 14:54:43 -0400221 // Mutating coords should work. (skbug.com/10918)
222 {
223 effect.start();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400224 Parameter p(kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400225 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400234 Parameter p1(kInOut_Modifier, kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400235 Function moveCoords(kVoid_Type, "moveCoords", p1);
236 moveCoords.define(
237 p1 -= 0.5
238 );
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400239 Parameter p2(kFloat2_Type, "p");
Ethan Nicholas624a5292021-04-16 14:54:43 -0400240 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();
Ethan Nicholasa2d22b22021-07-15 10:35:54 -0400255 GlobalVar child(kUniform_Modifier, kShader_Type, "child");
256 Declare(child);
257 Parameter p2(kFloat2_Type, "p");
Brian Osman552fcb92021-04-28 17:41:57 -0400258 Function(kFloat4_Type, "main", p2).define(
Brian Osmane76530d2021-09-09 14:31:31 -0400259 Return(child.eval(p2))
Ethan Nicholas624a5292021-04-16 14:54:43 -0400260 );
261 effect.end();
Ethan Nicholasd0f4d0d2021-06-23 13:51:55 -0400262 effect.child(child.name()) = nullptr;
Ethan Nicholas624a5292021-04-16 14:54:43 -0400263 effect.test(0xFF00FFFF,
264 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
265 }
266}
267
268DEF_TEST(DSLRuntimeEffectSimple, r) {
269 test_RuntimeEffect_Shaders(r, nullptr);
270}
271
272DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DSLRuntimeEffectSimple_GPU, r, ctxInfo) {
273 test_RuntimeEffect_Shaders(r, ctxInfo.directContext());
274}