xref: /aosp_15_r20/external/skia/fuzz/FuzzPrecompile.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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