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