Coverage Report

Created: 2024-09-14 07:19

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