blob: c31407a28da3390003d7e642e0829ff984116b4b [file] [log] [blame]
Brian Osman088913a2019-12-19 15:44:56 -05001/*
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
Brian Osmanf72dedd2020-01-08 13:19:58 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkPaint.h"
10#include "include/core/SkSurface.h"
Brian Osmanee426f22020-01-02 11:55:24 -050011#include "include/effects/SkRuntimeEffect.h"
Brian Osmanf72dedd2020-01-08 13:19:58 -050012#include "include/gpu/GrContext.h"
Brian Osman088913a2019-12-19 15:44:56 -050013#include "tests/Test.h"
14
Brian Osmanf72dedd2020-01-08 13:19:58 -050015#include <algorithm>
16
Brian Osman82d49702019-12-30 13:44:01 -050017DEF_TEST(SkRuntimeEffectInvalidInputs, r) {
18 auto test = [r](const char* hdr, const char* expected) {
19 SkString src = SkStringPrintf("%s void main(float x, float y, inout half4 color) {}", hdr);
Brian Osmanf72dedd2020-01-08 13:19:58 -050020 auto[effect, errorText] = SkRuntimeEffect::Make(src);
Brian Osman088913a2019-12-19 15:44:56 -050021 REPORTER_ASSERT(r, !effect);
22 REPORTER_ASSERT(r, errorText.contains(expected),
23 "Expected error message to contain \"%s\". Actual message: \"%s\"",
24 expected, errorText.c_str());
25 };
26
27 // Features that are only allowed in .fp files (key, in uniform, ctype, when, tracked).
28 // Ensure that these fail, and the error messages contain the relevant keyword.
Brian Osman82d49702019-12-30 13:44:01 -050029 test("layout(key) in bool Input;", "key");
30 test("in uniform float Input;", "in uniform");
31 test("layout(ctype=SkRect) float4 Input;", "ctype");
32 test("in bool Flag; layout(when=Flag) uniform float Input;", "when");
33 test("layout(tracked) uniform float Input;", "tracked");
Brian Osman088913a2019-12-19 15:44:56 -050034
Brian Osman82d49702019-12-30 13:44:01 -050035 // Runtime SkSL supports a limited set of uniform types. No samplers, for example:
36 test("uniform sampler2D s;", "sampler2D");
Brian Osman088913a2019-12-19 15:44:56 -050037
Brian Osman82d49702019-12-30 13:44:01 -050038 // 'in' variables can't be arrays
39 test("in int Input[2];", "array");
Brian Osman8783b782020-01-06 11:13:45 -050040
41 // Type specific restrictions:
42
43 // 'bool', 'int' can't be 'uniform'
44 test("uniform bool Input;", "'uniform'");
45 test("uniform int Input;", "'uniform'");
46
47 // vector and matrix types can't be 'in'
48 test("in float2 Input;", "'in'");
49 test("in half3x3 Input;", "'in'");
Brian Osman088913a2019-12-19 15:44:56 -050050}
Brian Osmanf72dedd2020-01-08 13:19:58 -050051
52// Our packing rules and unit test code here relies on this:
53static_assert(sizeof(bool) == 1);
54
55class TestEffect {
56public:
57 TestEffect(skiatest::Reporter* r, const char* hdr, const char* body) {
58 SkString src = SkStringPrintf("%s void main(float x, float y, inout half4 color) { %s }",
59 hdr, body);
60 auto[effect, errorText] = SkRuntimeEffect::Make(src);
61 if (!effect) {
62 REPORT_FAILURE(r, "effect",
63 SkStringPrintf("Effect didn't compile: %s", errorText.c_str()));
64 return;
65 }
66
67 fEffect = std::move(effect);
68 fInputs = SkData::MakeUninitialized(fEffect->inputSize());
69 }
70
71 struct InputVar {
72 template <typename T> InputVar& operator=(const T& val) {
73 SkASSERT(sizeof(T) == fVar.sizeInBytes());
74 memcpy(SkTAddOffset<void>(fOwner->fInputs->writable_data(), fVar.fOffset), &val,
75 sizeof(T));
76 return *this;
77 }
78 TestEffect* fOwner;
79 const SkRuntimeEffect::Variable& fVar;
80 };
81
82 InputVar operator[](const char* name) {
83 auto input = std::find_if(fEffect->inputs().begin(), fEffect->inputs().end(),
84 [name](const auto& v) { return v.fName.equals(name); });
85 SkASSERT(input != fEffect->inputs().end());
86 return {this, *input};
87 }
88
89 void test(skiatest::Reporter* r, sk_sp<SkSurface> surface,
90 uint32_t TL, uint32_t TR, uint32_t BL, uint32_t BR) {
91 if (!fEffect) { return; }
92
93 auto shader = fEffect->makeShader(fInputs, nullptr, 0, nullptr, false);
94 if (!shader) {
95 REPORT_FAILURE(r, "shader", SkString("Effect didn't produce a shader"));
96 return;
97 }
98
99 SkPaint paint;
100 paint.setShader(std::move(shader));
101 paint.setBlendMode(SkBlendMode::kSrc);
102 surface->getCanvas()->drawPaint(paint);
103
104 uint32_t actual[4];
105 SkImageInfo info = surface->imageInfo();
106 if (!surface->readPixels(info, actual, info.minRowBytes(), 0, 0)) {
107 REPORT_FAILURE(r, "readPixels", SkString("readPixels failed"));
108 return;
109 }
110
111 uint32_t expected[4] = {TL, TR, BL, BR};
112 if (memcmp(actual, expected, sizeof(actual)) != 0) {
113 REPORT_FAILURE(r, "Runtime effect didn't match expectations",
114 SkStringPrintf("\n"
115 "Expected: [ %08x %08x %08x %08x ]\n"
116 "Got : [ %08x %08x %08x %08x ]\n"
117 "SkSL:\n%s\n",
118 TL, TR, BL, BR, actual[0], actual[1], actual[2],
119 actual[3], fEffect->source().c_str()));
120 }
121 }
122
123 void test(skiatest::Reporter* r, sk_sp<SkSurface> surface, uint32_t expected) {
124 this->test(r, surface, expected, expected, expected, expected);
125 }
126
127private:
128 sk_sp<SkRuntimeEffect> fEffect;
129 sk_sp<SkData> fInputs;
130};
131
132static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrContext* context) {
133 SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
134 sk_sp<SkSurface> surface;
135 if (context) {
136 surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
137 } else {
138 surface = SkSurface::MakeRaster(info);
139 }
140 REPORTER_ASSERT(r, surface);
141
142 TestEffect xy(r, "", "color = half4(half(x - 0.5), half(y - 0.5), 0, 1);");
143 xy.test(r, surface, 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
144
Brian Osman504032e2020-01-10 10:05:24 -0500145 using float4 = std::array<float, 4>;
146
Brian Osmanf72dedd2020-01-08 13:19:58 -0500147 // NOTE: For now, we always emit valid premul colors, until CPU and GPU agree on clamping
148 TestEffect uniformColor(r, "uniform float4 gColor;", "color = half4(gColor);");
149
Brian Osman504032e2020-01-10 10:05:24 -0500150 uniformColor["gColor"] = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
Brian Osmanf72dedd2020-01-08 13:19:58 -0500151 uniformColor.test(r, surface, 0xFFBF4000);
152
Brian Osman504032e2020-01-10 10:05:24 -0500153 uniformColor["gColor"] = float4{ 0.75f, 0.25f, 0.0f, 1.0f };
Brian Osmanf72dedd2020-01-08 13:19:58 -0500154 uniformColor.test(r, surface, 0xFF0040BF);
Brian Osman504032e2020-01-10 10:05:24 -0500155
156 TestEffect pickColor(r, "in int flag; uniform half4 gColors[2];", "color = gColors[flag];");
157 pickColor["gColors"] =
158 std::array<float4, 2>{float4{1.0f, 0.0f, 0.0f, 1.0f}, float4{0.0f, 1.0f, 0.0f, 1.0f}};
159 pickColor["flag"] = 0;
160 pickColor.test(r, surface, 0xFF0000FF);
161 pickColor["flag"] = 1;
162 pickColor.test(r, surface, 0xFF00FF00);
Brian Osmanf72dedd2020-01-08 13:19:58 -0500163}
164
165DEF_TEST(SkRuntimeEffectSimple, r) {
166 test_RuntimeEffect_Shaders(r, nullptr);
167}
168
169DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectSimple_GPU, r, ctxInfo) {
170 test_RuntimeEffect_Shaders(r, ctxInfo.grContext());
171}