1 /*
2 * Copyright 2023 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 "fuzz/Fuzz.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPathBuilder.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/effects/SkColorMatrix.h"
19 #include "include/gpu/graphite/Context.h"
20 #include "include/gpu/graphite/PrecompileContext.h"
21 #include "include/gpu/graphite/Surface.h"
22 #include "include/gpu/graphite/precompile/Precompile.h"
23 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
24 #include "modules/skcms/skcms.h"
25 #include "src/core/SkBlenderBase.h"
26 #include "src/gpu/graphite/ContextPriv.h"
27 #include "src/gpu/graphite/ContextUtils.h"
28 #include "src/gpu/graphite/KeyContext.h"
29 #include "src/gpu/graphite/PaintParams.h"
30 #include "src/gpu/graphite/PaintParamsKey.h"
31 #include "src/gpu/graphite/PipelineData.h"
32 #include "src/gpu/graphite/RecorderPriv.h"
33 #include "src/gpu/graphite/RenderPassDesc.h"
34 #include "src/gpu/graphite/Renderer.h"
35 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
36 #include "src/gpu/graphite/geom/Geometry.h"
37 #include "src/gpu/graphite/precompile/PaintOptionsPriv.h"
38 #include "tools/gpu/GrContextFactory.h"
39 #include "tools/graphite/ContextFactory.h"
40
41 using namespace skgpu::graphite;
42
43 namespace {
44
random_blend_mode(Fuzz * fuzz)45 SkBlendMode random_blend_mode(Fuzz* fuzz) {
46 uint32_t temp;
47 fuzz->next(&temp);
48 return (SkBlendMode) (temp % kSkBlendModeCount);
49 }
50
random_opaque_skcolor(Fuzz * fuzz)51 SkColor random_opaque_skcolor(Fuzz* fuzz) {
52 SkColor color;
53 fuzz->next(&color);
54 return 0xff000000 | color;
55 }
56
random_color4f(Fuzz * fuzz)57 SkColor4f random_color4f(Fuzz* fuzz) {
58 bool makeOpaque;
59 fuzz->next(&makeOpaque);
60
61 SkColor4f color;
62 fuzz->nextRange(&color.fR, 0, 1);
63 fuzz->nextRange(&color.fG, 0, 1);
64 fuzz->nextRange(&color.fB, 0, 1);
65 if (makeOpaque) {
66 color.fA = 1.0;
67 } else {
68 fuzz->nextRange(&color.fA, 0, 1);
69 }
70
71 return color;
72 }
73
make_path()74 SkPath make_path() {
75 SkPathBuilder path;
76 path.moveTo(0, 0);
77 path.lineTo(8, 2);
78 path.lineTo(16, 0);
79 path.lineTo(14, 8);
80 path.lineTo(16, 16);
81 path.lineTo(8, 14);
82 path.lineTo(0, 16);
83 path.lineTo(2, 8);
84 path.close();
85 return path.detach();
86 }
87
88 //--------------------------------------------------------------------------------------------------
89 // color spaces
90
random_transfer_function(Fuzz * fuzz)91 const skcms_TransferFunction& random_transfer_function(Fuzz* fuzz) {
92 static constexpr skcms_TransferFunction gTransferFunctions[] = {
93 SkNamedTransferFn::kSRGB,
94 SkNamedTransferFn::k2Dot2,
95 SkNamedTransferFn::kLinear,
96 SkNamedTransferFn::kRec2020,
97 SkNamedTransferFn::kPQ,
98 SkNamedTransferFn::kHLG,
99 };
100
101 uint32_t xferFunction;
102 fuzz->next(&xferFunction);
103 xferFunction %= std::size(gTransferFunctions);
104 return gTransferFunctions[xferFunction];
105 }
106
random_gamut(Fuzz * fuzz)107 const skcms_Matrix3x3& random_gamut(Fuzz* fuzz) {
108 static constexpr skcms_Matrix3x3 gGamuts[] = {
109 SkNamedGamut::kSRGB,
110 SkNamedGamut::kAdobeRGB,
111 SkNamedGamut::kDisplayP3,
112 SkNamedGamut::kRec2020,
113 SkNamedGamut::kXYZ,
114 };
115
116 uint32_t gamut;
117 fuzz->next(&gamut);
118 gamut %= std::size(gGamuts);
119 return gGamuts[gamut];
120 }
121
122 enum class ColorSpaceType {
123 kNone,
124 kSRGB,
125 kSRGBLinear,
126 kRGB,
127
128 kLast = kRGB
129 };
130
131 static constexpr int kColorSpaceTypeCount = static_cast<int>(ColorSpaceType::kLast) + 1;
132
create_colorspace(Fuzz * fuzz,ColorSpaceType csType)133 sk_sp<SkColorSpace> create_colorspace(Fuzz* fuzz, ColorSpaceType csType) {
134 switch (csType) {
135 case ColorSpaceType::kNone:
136 return nullptr;
137 case ColorSpaceType::kSRGB:
138 return SkColorSpace::MakeSRGB();
139 case ColorSpaceType::kSRGBLinear:
140 return SkColorSpace::MakeSRGBLinear();
141 case ColorSpaceType::kRGB:
142 return SkColorSpace::MakeRGB(random_transfer_function(fuzz), random_gamut(fuzz));
143 }
144
145 SkUNREACHABLE;
146 }
147
create_random_colorspace(Fuzz * fuzz)148 sk_sp<SkColorSpace> create_random_colorspace(Fuzz* fuzz) {
149 uint32_t temp;
150 fuzz->next(&temp);
151 ColorSpaceType csType = (ColorSpaceType) (temp % kColorSpaceTypeCount);
152
153 return create_colorspace(fuzz, csType);
154 }
155
156 //--------------------------------------------------------------------------------------------------
157 // color filters
158
159 enum class ColorFilterType {
160 kNone,
161 kBlend,
162 kMatrix,
163 kHSLAMatrix,
164 // TODO: add more color filters
165
166 kLast = kHSLAMatrix
167 };
168
169 static constexpr int kColorFilterTypeCount = static_cast<int>(ColorFilterType::kLast) + 1;
170
create_blend_colorfilter(Fuzz * fuzz)171 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_blend_colorfilter(
172 Fuzz* fuzz) {
173
174 sk_sp<SkColorFilter> cf;
175
176 // SkColorFilters::Blend is clever and can weed out noop color filters. Loop until we get
177 // a valid color filter.
178 while (!cf && !fuzz->exhausted()) {
179 cf = SkColorFilters::Blend(random_color4f(fuzz),
180 create_random_colorspace(fuzz),
181 random_blend_mode(fuzz));
182 }
183
184 sk_sp<PrecompileColorFilter> o = cf ? PrecompileColorFilters::Blend() : nullptr;
185
186 return { cf, o };
187 }
188
create_matrix_colorfilter()189 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_matrix_colorfilter() {
190 sk_sp<SkColorFilter> cf = SkColorFilters::Matrix(
191 SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
192 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Matrix();
193
194 return { cf, o };
195 }
196
create_hsla_matrix_colorfilter()197 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_hsla_matrix_colorfilter() {
198 sk_sp<SkColorFilter> cf = SkColorFilters::HSLAMatrix(
199 SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
200 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::HSLAMatrix();
201
202 return { cf, o };
203 }
204
create_colorfilter(Fuzz * fuzz,ColorFilterType type,int depth)205 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_colorfilter(
206 Fuzz* fuzz,
207 ColorFilterType type,
208 int depth) {
209 if (depth <= 0) {
210 return {};
211 }
212
213 switch (type) {
214 case ColorFilterType::kNone:
215 return { nullptr, nullptr };
216 case ColorFilterType::kBlend:
217 return create_blend_colorfilter(fuzz);
218 case ColorFilterType::kMatrix:
219 return create_matrix_colorfilter();
220 case ColorFilterType::kHSLAMatrix:
221 return create_hsla_matrix_colorfilter();
222 }
223
224 SkUNREACHABLE;
225 }
226
create_random_colorfilter(Fuzz * fuzz,int depth)227 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_random_colorfilter(
228 Fuzz* fuzz,
229 int depth) {
230
231 uint32_t temp;
232 fuzz->next(&temp);
233 ColorFilterType cf = (ColorFilterType) (temp % kColorFilterTypeCount);
234
235 return create_colorfilter(fuzz, cf, depth);
236 }
237
238 //--------------------------------------------------------------------------------------------------
create_random_paint(Fuzz * fuzz,int depth)239 std::pair<SkPaint, PaintOptions> create_random_paint(Fuzz* fuzz, int depth) {
240 if (depth <= 0) {
241 return {};
242 }
243
244 SkPaint paint;
245 paint.setColor(random_opaque_skcolor(fuzz));
246
247 PaintOptions paintOptions;
248
249 {
250 auto [cf, o] = create_random_colorfilter(fuzz, depth - 1);
251 SkASSERT_RELEASE(!cf == !o);
252
253 if (cf) {
254 paint.setColorFilter(std::move(cf));
255 paintOptions.setColorFilters({o});
256 }
257 }
258
259 return { paint, paintOptions };
260 }
261
262 //--------------------------------------------------------------------------------------------------
check_draw(Context * context,Recorder * recorder,const SkPaint & paint,DrawTypeFlags dt,const SkPath & path)263 void check_draw(Context* context,
264 Recorder* recorder,
265 const SkPaint& paint,
266 DrawTypeFlags dt,
267 const SkPath& path) {
268 int before = context->priv().globalCache()->numGraphicsPipelines();
269
270 {
271 // TODO: vary the colorType of the target surface too
272 SkImageInfo ii = SkImageInfo::Make(16, 16,
273 kRGBA_8888_SkColorType,
274 kPremul_SkAlphaType);
275
276 sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(recorder, ii);
277 SkCanvas* canvas = surf->getCanvas();
278
279 switch (dt) {
280 case DrawTypeFlags::kSimpleShape:
281 canvas->drawRect(SkRect::MakeWH(16, 16), paint);
282 break;
283 case DrawTypeFlags::kNonSimpleShape:
284 canvas->drawPath(path, paint);
285 break;
286 default:
287 SkASSERT_RELEASE(false);
288 break;
289 }
290
291 std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
292 context->insertRecording({ recording.get() });
293 context->submit(SyncToCpu::kYes);
294 }
295
296 int after = context->priv().globalCache()->numGraphicsPipelines();
297
298 // Actually using the SkPaint with the specified type of draw shouldn't have caused
299 // any additional compilation
300 SkASSERT_RELEASE(before == after);
301 }
302
fuzz_graphite(Fuzz * fuzz,Context * context,int depth=9)303 void fuzz_graphite(Fuzz* fuzz, Context* context, int depth = 9) {
304 std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
305 std::unique_ptr<Recorder> recorder = context->makeRecorder();
306 ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
307
308 SkColorInfo ci = SkColorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType,
309 SkColorSpace::MakeSRGB());
310
311 std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
312 KeyContext precompileKeyContext(recorder->priv().caps(), dict, rtDict.get(), ci);
313
314 DrawTypeFlags kDrawType = DrawTypeFlags::kSimpleShape;
315 SkPath path = make_path();
316
317 Layout layout = context->backend() == skgpu::BackendApi::kMetal ? Layout::kMetal
318 : Layout::kStd140;
319
320 PaintParamsKeyBuilder builder(dict);
321 PipelineDataGatherer gatherer(layout);
322
323 auto [paint, paintOptions] = create_random_paint(fuzz, depth);
324
325 constexpr Coverage coverageOptions[3] = {
326 Coverage::kNone, Coverage::kSingleChannel, Coverage::kLCD};
327 uint32_t temp;
328 fuzz->next(&temp);
329 Coverage coverage = coverageOptions[temp % 3];
330
331 DstReadRequirement dstReadReq = DstReadRequirement::kNone;
332 const SkBlenderBase* blender = as_BB(paint.getBlender());
333 if (blender) {
334 dstReadReq = GetDstReadRequirement(recorder->priv().caps(),
335 blender->asBlendMode(),
336 coverage);
337 }
338
339 UniquePaintParamsID paintID = ExtractPaintData(recorder.get(),
340 &gatherer,
341 &builder,
342 layout,
343 {},
344 PaintParams(paint,
345 /* primitiveBlender= */ nullptr,
346 /* analyticClip= */ {},
347 /* clipShader= */ nullptr,
348 dstReadReq,
349 /* skipColorXform= */ false),
350 {},
351 ci);
352
353 RenderPassDesc unusedRenderPassDesc;
354
355 std::vector<UniquePaintParamsID> precompileIDs;
356 paintOptions.priv().buildCombinations(precompileKeyContext,
357 &gatherer,
358 DrawTypeFlags::kNone,
359 /* withPrimitiveBlender= */ false,
360 coverage,
361 unusedRenderPassDesc,
362 [&](UniquePaintParamsID id,
363 DrawTypeFlags,
364 bool /* withPrimitiveBlender */,
365 Coverage,
366 const RenderPassDesc&) {
367 precompileIDs.push_back(id);
368 });
369
370 // The specific key generated by ExtractPaintData should be one of the
371 // combinations generated by the combination system.
372 auto result = std::find(precompileIDs.begin(), precompileIDs.end(), paintID);
373
374 #ifdef SK_DEBUG
375 if (result == precompileIDs.end()) {
376 SkDebugf("From paint: ");
377 dict->dump(paintID);
378
379 SkDebugf("From combination builder:");
380 for (auto iter : precompileIDs) {
381 dict->dump(iter);
382 }
383 }
384 #endif
385
386 SkASSERT_RELEASE(result != precompileIDs.end());
387
388 {
389 static const RenderPassProperties kDefaultRenderPassProperties;
390
391 context->priv().globalCache()->resetGraphicsPipelines();
392
393 int before = context->priv().globalCache()->numGraphicsPipelines();
394 Precompile(precompileContext.get(), paintOptions, kDrawType,
395 { kDefaultRenderPassProperties });
396 int after = context->priv().globalCache()->numGraphicsPipelines();
397
398 SkASSERT_RELEASE(before == 0);
399 SkASSERT_RELEASE(after > before);
400
401 check_draw(context, recorder.get(), paint, kDrawType, path);
402 }
403 }
404
405 } // anonymous namespace
406
DEF_FUZZ(Precompile,fuzz)407 DEF_FUZZ(Precompile, fuzz) {
408 skiatest::graphite::ContextFactory factory;
409
410 skgpu::ContextType contextType;
411 #if defined(SK_METAL)
412 contextType = skgpu::ContextType::kMetal;
413 #elif defined(SK_VULKAN)
414 contextType = skgpu::ContextType::kVulkan;
415 #else
416 contextType = skgpu::ContextType::kMock;
417 #endif
418
419 skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(contextType);
420 skgpu::graphite::Context* context = ctxInfo.fContext;
421 if (!context) {
422 return;
423 }
424
425 fuzz_graphite(fuzz, context);
426 }
427