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