Coverage Report

Created: 2024-05-20 07:14

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