Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FuzzPrecompile.cpp
Go to the documentation of this file.
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
13#include "include/core/SkFont.h"
21#include "modules/skcms/skcms.h"
36#include "tools/ToolUtils.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) {
52 fuzz->next(&color);
53 return 0xff000000 | color;
54}
55
56SkColor4f random_color4f(Fuzz* fuzz) {
57 bool makeOpaque;
58 fuzz->next(&makeOpaque);
59
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
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
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[] = {
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[] = {
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:
146 case ColorSpaceType::kRGB:
147 return SkColorSpace::MakeRGB(random_transfer_function(fuzz), random_gamut(fuzz));
148 }
149
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
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() {
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() {
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
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
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,
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();
309
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 auto dstTexInfo = recorder->priv().caps()->getDefaultSampledTextureInfo(kRGBA_8888_SkColorType,
322 skgpu::Mipmapped::kNo,
323 skgpu::Protected::kNo,
324 skgpu::Renderable::kNo);
325 // Use Budgeted::kYes to avoid immediately instantiating the TextureProxy. This test doesn't
326 // require full resources.
327 sk_sp<TextureProxy> fakeDstTexture = TextureProxy::Make(recorder->priv().caps(),
328 recorder->priv().resourceProvider(),
329 SkISize::Make(1, 1),
330 dstTexInfo,
332 constexpr SkIPoint fakeDstOffset = SkIPoint::Make(0, 0);
333
334 DrawTypeFlags kDrawType = DrawTypeFlags::kShape;
336
337 Layout layout = context->backend() == skgpu::BackendApi::kMetal ? Layout::kMetal
338 : Layout::kStd140;
339
341 PipelineDataGatherer gatherer(layout);
342
343
344 auto [paint, paintOptions] = create_random_paint(fuzz, depth);
345
346 constexpr Coverage coverageOptions[3] = {
347 Coverage::kNone, Coverage::kSingleChannel, Coverage::kLCD};
348 uint32_t temp;
349 fuzz->next(&temp);
350 Coverage coverage = coverageOptions[temp % 3];
351
352 DstReadRequirement dstReadReq = DstReadRequirement::kNone;
353 const SkBlenderBase* blender = as_BB(paint.getBlender());
354 if (blender) {
355 dstReadReq = GetDstReadRequirement(recorder->priv().caps(),
356 blender->asBlendMode(),
357 coverage);
358 }
359 bool needsDstSample = dstReadReq == DstReadRequirement::kTextureCopy ||
360 dstReadReq == DstReadRequirement::kTextureSample;
361 sk_sp<TextureProxy> curDst = needsDstSample ? fakeDstTexture : nullptr;
362
363 auto [paintID, uData, tData] = ExtractPaintData(
364 recorder.get(), &gatherer, &builder, layout, {},
366 /* primitiveBlender= */ nullptr,
367 /* clipShader= */nullptr,
368 dstReadReq,
369 /* skipColorXform= */ false),
370 curDst, fakeDstOffset, ci);
371
372 std::vector<UniquePaintParamsID> precompileIDs;
373 paintOptions.priv().buildCombinations(precompileKeyContext,
374 &gatherer,
375 DrawTypeFlags::kNone,
376 /* withPrimitiveBlender= */ false,
377 coverage,
378 [&](UniquePaintParamsID id,
380 bool /* withPrimitiveBlender */,
381 Coverage) {
382 precompileIDs.push_back(id);
383 });
384
385 // The specific key generated by ExtractPaintData should be one of the
386 // combinations generated by the combination system.
387 auto result = std::find(precompileIDs.begin(), precompileIDs.end(), paintID);
388
389#ifdef SK_DEBUG
390 if (result == precompileIDs.end()) {
391 SkDebugf("From paint: ");
392 dump(dict, paintID);
393
394 SkDebugf("From combination builder:");
395 for (auto iter : precompileIDs) {
396 dump(dict, iter);
397 }
398 }
399#endif
400
401 SkASSERT_RELEASE(result != precompileIDs.end());
402
403 {
404 context->priv().globalCache()->resetGraphicsPipelines();
405
406 int before = context->priv().globalCache()->numGraphicsPipelines();
407 Precompile(context, paintOptions, kDrawType);
408 int after = context->priv().globalCache()->numGraphicsPipelines();
409
410 SkASSERT_RELEASE(before == 0);
411 SkASSERT_RELEASE(after > before);
412
413 check_draw(context, recorder.get(), paint, kDrawType, path);
414 }
415}
416
417} // anonymous namespace
418
421
422 skgpu::ContextType contextType;
423#if defined(SK_METAL)
424 contextType = skgpu::ContextType::kMetal;
425#elif defined(SK_VULKAN)
426 contextType = skgpu::ContextType::kVulkan;
427#else
428 contextType = skgpu::ContextType::kMock;
429#endif
430
431 skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(contextType);
432 skgpu::graphite::Context* context = ctxInfo.fContext;
433 if (!context) {
434 return;
435 }
436
437 fuzz_graphite(fuzz, context);
438}
#define DEF_FUZZ(name, f)
Definition Fuzz.h:156
SkColor4f color
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT_RELEASE(cond)
Definition SkAssert.h:100
static constexpr int kSkBlendModeCount
Definition SkBlendMode.h:76
SkBlendMode
Definition SkBlendMode.h:38
SkBlenderBase * as_BB(SkBlender *blend)
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
uint32_t SkColor
Definition SkColor.h:37
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition SkImageInfo.h:69
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Definition Fuzz.h:24
void next(T *t)
Definition Fuzz.h:64
void nextRange(T *, Min, Max)
Definition Fuzz.h:119
bool exhausted() const
Definition Fuzz.h:39
virtual std::optional< SkBlendMode > asBlendMode() const
void drawRect(const SkRect &rect, const SkPaint &paint)
void drawPath(const SkPath &path, const SkPaint &paint)
static sk_sp< SkColorFilter > Blend(const SkColor4f &c, sk_sp< SkColorSpace >, SkBlendMode mode)
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
static sk_sp< SkColorFilter > HSLAMatrix(const SkColorMatrix &)
static SkColorMatrix RGBtoYUV(SkYUVColorSpace)
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkColorSpace > MakeRGB(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &toXYZ)
static sk_sp< SkColorSpace > MakeSRGBLinear()
virtual TextureInfo getDefaultSampledTextureInfo(SkColorType, Mipmapped mipmapped, Protected, Renderable) const =0
const GlobalCache * globalCache() const
Definition ContextPriv.h:40
const ShaderCodeDictionary * shaderCodeDictionary() const
Definition ContextPriv.h:34
BackendApi backend() const
Definition Context.cpp:128
std::unique_ptr< Recorder > makeRecorder(const RecorderOptions &={})
Definition Context.cpp:130
bool submit(SyncToCpu=SyncToCpu::kNo)
Definition Context.cpp:148
bool insertRecording(const InsertRecordingInfo &)
Definition Context.cpp:142
void buildCombinations(const KeyContext &keyContext, PipelineDataGatherer *gatherer, DrawTypeFlags drawTypes, bool withPrimitiveBlender, Coverage coverage, const PaintOptions::ProcessCombination &processCombination) const
void setColorFilters(SkSpan< const sk_sp< PrecompileColorFilter > > colorFilters)
Definition Precompile.h:180
const Caps * caps() const
ResourceProvider * resourceProvider()
std::unique_ptr< Recording > snap()
Definition Recorder.cpp:149
PaintParamsKey lookup(UniquePaintParamsID) const SK_EXCLUDES(fSpinLock)
static sk_sp< TextureProxy > Make(const Caps *, ResourceProvider *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted)
ContextInfo getContextInfo(skgpu::ContextType)
const Paint & paint
static const skcms_Matrix3x3 gGamuts[]
GAsyncResult * result
static constexpr skcms_Matrix3x3 kSRGB
static constexpr skcms_Matrix3x3 kAdobeRGB
static constexpr skcms_Matrix3x3 kXYZ
static constexpr skcms_Matrix3x3 kRec2020
static constexpr skcms_Matrix3x3 kDisplayP3
static constexpr skcms_TransferFunction kRec2020
static constexpr skcms_TransferFunction k2Dot2
static constexpr skcms_TransferFunction kSRGB
static constexpr skcms_TransferFunction kHLG
static constexpr skcms_TransferFunction kPQ
static constexpr skcms_TransferFunction kLinear
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DstReadRequirement GetDstReadRequirement(const Caps *caps, std::optional< SkBlendMode > blendMode, Coverage coverage)
bool Precompile(Context *context, RuntimeEffectDictionary *rteDict, const GraphicsPipelineDesc &pipelineDesc, const RenderPassDesc &renderPassDesc)
std::tuple< UniquePaintParamsID, const UniformDataBlock *, const TextureDataBlock * > ExtractPaintData(Recorder *recorder, PipelineDataGatherer *gatherer, PaintParamsKeyBuilder *builder, const Layout layout, const SkM44 &local2Dev, const PaintParams &p, sk_sp< TextureProxy > dstTexture, SkIPoint dstOffset, const SkColorInfo &targetColorInfo)
static SkPath make_path()
static constexpr SkIPoint Make(int32_t x, int32_t y)
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
skgpu::graphite::Context * fContext