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