1 /*
2 * Copyright 2022 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 "tests/Test.h"
9
10 #if defined(SK_GRAPHITE)
11
12 #include "include/core/SkColorSpace.h"
13 #include "include/effects/SkRuntimeEffect.h"
14 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
15 #include "include/gpu/graphite/precompile/PrecompileShader.h"
16 #include "src/gpu/graphite/ContextPriv.h"
17 #include "src/gpu/graphite/FactoryFunctions.h"
18 #include "src/gpu/graphite/KeyContext.h"
19 #include "src/gpu/graphite/PipelineData.h"
20 #include "src/gpu/graphite/PrecompileInternal.h"
21 #include "src/gpu/graphite/Renderer.h"
22 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
23 #include "src/gpu/graphite/precompile/PaintOptionsPriv.h"
24
25 #include <array>
26
27 using namespace::skgpu::graphite;
28
29 namespace {
30
31 // A default kSrcOver blend mode will be supplied if no other blend options are added
no_blend_mode_option_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,skiatest::Reporter * reporter)32 void no_blend_mode_option_test(const KeyContext& keyContext,
33 PipelineDataGatherer* gatherer,
34 skiatest::Reporter* reporter) {
35 PaintOptions paintOptions;
36 paintOptions.setShaders({ PrecompileShaders::Color() });
37
38 REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 1);
39
40 std::vector<UniquePaintParamsID> precompileIDs;
41 paintOptions.priv().buildCombinations(keyContext,
42 gatherer,
43 DrawTypeFlags::kNone,
44 /* withPrimitiveBlender= */ false,
45 Coverage::kNone,
46 [&precompileIDs](UniquePaintParamsID id,
47 DrawTypeFlags,
48 bool /* withPrimitiveBlender */,
49 Coverage) {
50 precompileIDs.push_back(id);
51 });
52
53 SkASSERT(precompileIDs.size() == 1);
54 }
55
56 // This test checks that the 'PaintOptions::numCombinations' method and the number actually
57 // generated by 'buildCombinations' agree with the expected number of combinations.
run_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,skiatest::Reporter * reporter,const PaintOptions & paintOptions,int expectedNumOptions)58 void run_test(const KeyContext& keyContext,
59 PipelineDataGatherer* gatherer,
60 skiatest::Reporter* reporter,
61 const PaintOptions& paintOptions,
62 int expectedNumOptions) {
63
64 REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == expectedNumOptions);
65
66 std::vector<UniquePaintParamsID> precompileIDs;
67 paintOptions.priv().buildCombinations(keyContext,
68 gatherer,
69 DrawTypeFlags::kNone,
70 /* withPrimitiveBlender= */ false,
71 Coverage::kNone,
72 [&precompileIDs](UniquePaintParamsID id,
73 DrawTypeFlags,
74 bool /* withPrimitiveBlender */,
75 Coverage) {
76 precompileIDs.push_back(id);
77 });
78
79 SkASSERT(static_cast<int>(precompileIDs.size()) == expectedNumOptions);
80 }
81
big_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,skiatest::Reporter * reporter)82 void big_test(const KeyContext& keyContext,
83 PipelineDataGatherer* gatherer,
84 skiatest::Reporter* reporter) {
85 // paintOptions (248)
86 // |- sweepGrad_0 (2) | blendShader_0 (60)
87 // | 0: kSrc (1)
88 // | 1: (dsts) linearGrad_0 (2) | solid_0 (1)
89 // | 2: (srcs) linearGrad_1 (2) | blendShader_1 (18)
90 // | 0: kDst (1)
91 // | 1: (dsts) radGrad_0 (2) | solid_1 (1)
92 // | 2: (srcs) imageShader_0 (6)
93 // |
94 // |- 4-built-in-blend-modes
95
96 PaintOptions paintOptions;
97
98 // first, shaders. First top-level option (sweepGrad_0)
99 sk_sp<PrecompileShader> sweepGrad_0 = PrecompileShaders::SweepGradient();
100
101 std::array<SkBlendMode, 1> blendModes{ SkBlendMode::kSrc };
102
103 std::vector<SkBlendMode> moreBlendModes{ SkBlendMode::kDst };
104
105 // Second top-level option (blendShader_0)
106 auto blendShader_0 = PrecompileShaders::Blend(
107 SkSpan<const SkBlendMode>(blendModes), // std::array
108 { // initializer_list
109 PrecompileShaders::LinearGradient(),
110 PrecompileShaders::Color()
111 },
112 {
113 PrecompileShaders::LinearGradient(),
114 PrecompileShaders::Blend(
115 SkSpan<const SkBlendMode>(moreBlendModes),// std::vector
116 {
117 PrecompileShaders::RadialGradient(),
118 PrecompileShaders::Color()
119 },
120 {
121 PrecompileShaders::Image()
122 })
123 });
124
125 paintOptions.setShaders({ sweepGrad_0, blendShader_0 });
126
127 static const SkBlendMode kEvenMoreBlendModes[] = {
128 SkBlendMode::kSrcOver,
129 SkBlendMode::kSrc,
130 SkBlendMode::kDstOver,
131 SkBlendMode::kDst
132 };
133
134 // now, blend modes
135 paintOptions.setBlendModes(kEvenMoreBlendModes); // c array
136
137 REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 248,
138 "Actual # of combinations %d", paintOptions.priv().numCombinations());
139
140 std::vector<UniquePaintParamsID> precompileIDs;
141 paintOptions.priv().buildCombinations(keyContext,
142 gatherer,
143 DrawTypeFlags::kNone,
144 /* withPrimitiveBlender= */ false,
145 Coverage::kNone,
146 [&precompileIDs](UniquePaintParamsID id,
147 DrawTypeFlags,
148 bool /* withPrimitiveBlender */,
149 Coverage) {
150 precompileIDs.push_back(id);
151 });
152
153 SkASSERT(precompileIDs.size() == 248);
154 }
155
156 template <typename T>
create_runtime_combos(skiatest::Reporter * reporter,SkRuntimeEffect::Result effectFactory (SkString),sk_sp<T> precompileFactory (sk_sp<SkRuntimeEffect>,SkSpan<const PrecompileChildOptions> childOptions),const char * redCode,const char * greenCode,const char * combineCode)157 std::vector<sk_sp<T>> create_runtime_combos(
158 skiatest::Reporter* reporter,
159 SkRuntimeEffect::Result effectFactory(SkString),
160 sk_sp<T> precompileFactory(sk_sp<SkRuntimeEffect>,
161 SkSpan<const PrecompileChildOptions> childOptions),
162 const char* redCode,
163 const char* greenCode,
164 const char* combineCode) {
165 auto [redEffect, error1] = effectFactory(SkString(redCode));
166 REPORTER_ASSERT(reporter, redEffect, "%s", error1.c_str());
167 auto [greenEffect, error2] = effectFactory(SkString(greenCode));
168 REPORTER_ASSERT(reporter, greenEffect, "%s", error2.c_str());
169 auto [combineEffect, error3] = effectFactory(SkString(combineCode));
170 REPORTER_ASSERT(reporter, combineEffect, "%s", error3.c_str());
171
172 sk_sp<T> red = precompileFactory(redEffect, {});
173 REPORTER_ASSERT(reporter, red);
174
175 sk_sp<T> green = precompileFactory(greenEffect, {});
176 REPORTER_ASSERT(reporter, green);
177
178 sk_sp<T> combine = precompileFactory(combineEffect, { { red, green }, { green, red } });
179 REPORTER_ASSERT(reporter, combine);
180
181 return { combine };
182 }
183
runtime_effect_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,skiatest::Reporter * reporter)184 void runtime_effect_test(const KeyContext& keyContext,
185 PipelineDataGatherer* gatherer,
186 skiatest::Reporter* reporter) {
187 // paintOptions (8)
188 // |- combineShader (2)
189 // | 0: redShader | greenShader
190 // | 1: greenShader | redShader
191 // |
192 // |- combineColorFilter (2)
193 // | 0: redColorFilter | greenColorFilter
194 // | 1: greenColorFilter | redColorFilter
195 // |
196 // |- combineBlender (2)
197 // | 0: redBlender | greenBlender
198 // | 1: greenBlender | redBlender
199
200 PaintOptions paintOptions;
201
202 // shaders
203 {
204 static const char* kRedS = R"(
205 half4 main(vec2 fragcoord) { return half4(.5, 0, 0, .5); }
206 )";
207 static const char* kGreenS = R"(
208 half4 main(vec2 fragcoord) { return half4(0, .5, 0, .5); }
209 )";
210
211 static const char* kCombineS = R"(
212 uniform shader first;
213 uniform shader second;
214 half4 main(vec2 fragcoords) {
215 return first.eval(fragcoords) + second.eval(fragcoords);
216 }
217 )";
218
219 std::vector<sk_sp<PrecompileShader>> combinations =
220 create_runtime_combos<PrecompileShader>(reporter,
221 SkRuntimeEffect::MakeForShader,
222 MakePrecompileShader,
223 kRedS,
224 kGreenS,
225 kCombineS);
226 paintOptions.setShaders(combinations);
227 }
228
229 // color filters
230 {
231 static const char* kRedCF = R"(
232 half4 main(half4 color) { return half4(.5, 0, 0, .5); }
233 )";
234 static const char* kGreenCF = R"(
235 half4 main(half4 color) { return half4(0, .5, 0, .5); }
236 )";
237
238 static const char* kCombineCF = R"(
239 uniform colorFilter first;
240 uniform colorFilter second;
241 half4 main(half4 color) { return first.eval(color) + second.eval(color); }
242 )";
243
244 std::vector<sk_sp<PrecompileColorFilter>> combinations =
245 create_runtime_combos<PrecompileColorFilter>(reporter,
246 SkRuntimeEffect::MakeForColorFilter,
247 MakePrecompileColorFilter,
248 kRedCF,
249 kGreenCF,
250 kCombineCF);
251 paintOptions.setColorFilters(combinations);
252 }
253
254 // blenders
255 {
256 static const char* kRedB = R"(
257 half4 main(half4 src, half4 dst) { return half4(.5, 0, 0, .5); }
258 )";
259 static const char* kGreenB = R"(
260 half4 main(half4 src, half4 dst) { return half4(0, .5, 0, .5); }
261 )";
262
263 static const char* kCombineB = R"(
264 uniform blender first;
265 uniform blender second;
266 half4 main(half4 src, half4 dst) {
267 return first.eval(src, dst) + second.eval(src, dst);
268 }
269 )";
270
271 std::vector<sk_sp<PrecompileBlender>> combinations =
272 create_runtime_combos<PrecompileBlender>(reporter,
273 SkRuntimeEffect::MakeForBlender,
274 MakePrecompileBlender,
275 kRedB,
276 kGreenB,
277 kCombineB);
278 paintOptions.setBlenders(combinations);
279 }
280
281 REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 8);
282
283 std::vector<UniquePaintParamsID> precompileIDs;
284 paintOptions.priv().buildCombinations(keyContext,
285 gatherer,
286 DrawTypeFlags::kNone,
287 /* withPrimitiveBlender= */ false,
288 Coverage::kNone,
289 [&precompileIDs](UniquePaintParamsID id,
290 DrawTypeFlags,
291 bool /* withPrimitiveBlender */,
292 Coverage) {
293 precompileIDs.push_back(id);
294 });
295
296 SkASSERT(precompileIDs.size() == 8);
297 }
298
299 } // anonymous namespace
300
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(CombinationBuilderTest,reporter,context,CtsEnforcement::kNextRelease)301 DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(CombinationBuilderTest, reporter, context,
302 CtsEnforcement::kNextRelease) {
303 ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
304
305 auto rtEffectDict = std::make_unique<RuntimeEffectDictionary>();
306
307 SkColorInfo ci(kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
308 KeyContext keyContext(context->priv().caps(),
309 dict,
310 rtEffectDict.get(),
311 ci,
312 /* dstTexture= */ nullptr,
313 /* dstOffset= */ {0, 0});
314
315 PipelineDataGatherer gatherer(context->priv().caps(), Layout::kMetal);
316
317 // The default PaintOptions should create a single combination with a solid color shader and
318 // kSrcOver blending
319 {
320 PaintOptions paintOptions;
321
322 run_test(keyContext, &gatherer, reporter, paintOptions, /* expectedNumOptions= */ 1);
323 }
324
325 // The BlendMode PrecompileBlender only ever has 1 combination
326 {
327 PaintOptions paintOptions;
328 paintOptions.setBlenders({ PrecompileBlenders::Mode(SkBlendMode::kColorDodge) });
329
330 run_test(keyContext, &gatherer, reporter, paintOptions, /* expectedNumOptions= */ 1);
331 }
332
333 // Specifying the BlendMode PrecompileBlender by SkBlendMode should also only ever
334 // yield 1 combination.
335 {
336 SkBlendMode blendModes[] = { SkBlendMode::kSrcOver };
337
338 PaintOptions paintOptions;
339 paintOptions.setBlendModes(blendModes);
340
341 run_test(keyContext, &gatherer, reporter, paintOptions, /* expectedNumOptions= */ 1);
342 }
343
344 // The Arithmetic PrecompileBlender only ever has 1 combination
345 {
346 PaintOptions paintOptions;
347 paintOptions.setBlenders({ PrecompileBlenders::Arithmetic() });
348
349 run_test(keyContext, &gatherer, reporter, paintOptions, /* expectedNumOptions= */ 1);
350 }
351
352 no_blend_mode_option_test(keyContext, &gatherer, reporter);
353 big_test(keyContext, &gatherer, reporter);
354 runtime_effect_test(keyContext, &gatherer, reporter);
355 }
356
357 #endif // SK_GRAPHITE
358