Flutter Engine
The Flutter Engine
PaintOptions.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2024 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
9
27
28namespace skgpu::graphite {
29
31PaintOptions::PaintOptions(const PaintOptions&) = default;
33PaintOptions& PaintOptions::operator=(const PaintOptions&) = default;
34
36 fShaderOptions.clear();
37 fShaderOptions.push_back_n(shaders.size(), shaders.data());
38}
39
41 fImageFilterOptions.clear();
42 fImageFilterOptions.push_back_n(imageFilters.size(), imageFilters.data());
43}
44
46 fMaskFilterOptions.clear();
47 fMaskFilterOptions.push_back_n(maskFilters.size(), maskFilters.data());
48}
49
51 fColorFilterOptions.clear();
52 fColorFilterOptions.push_back_n(colorFilters.size(), colorFilters.data());
53}
54
55void PaintOptions::addColorFilter(sk_sp<PrecompileColorFilter> cf) {
56 fColorFilterOptions.push_back(std::move(cf));
57}
58
60 fBlendModeOptions.clear();
61 fBlendModeOptions.push_back_n(blendModes.size(), blendModes.data());
62}
63
65 for (const sk_sp<PrecompileBlender>& b: blenders) {
66 if (b->priv().asBlendMode().has_value()) {
67 fBlendModeOptions.push_back(b->priv().asBlendMode().value());
68 } else {
69 fBlenderOptions.push_back(b);
70 }
71 }
72}
73
74void PaintOptions::setClipShaders(SkSpan<const sk_sp<PrecompileShader>> clipShaders) {
75 // In the normal API this modification happens in SkDevice::clipShader()
76 fClipShaderOptions.reserve(2 * clipShaders.size());
77 for (const sk_sp<PrecompileShader>& cs : clipShaders) {
78 // All clipShaders get wrapped in a CTMShader ...
79 sk_sp<PrecompileShader> withCTM = cs ? PrecompileShadersPriv::CTM({ cs }) : nullptr;
80 // and, if it is a SkClipOp::kDifference clip, an additional ColorFilterShader
82 withCTM ? withCTM->makeWithColorFilter(PrecompileColorFilters::Blend())
83 : nullptr;
84
85 fClipShaderOptions.emplace_back(std::move(withCTM));
86 fClipShaderOptions.emplace_back(std::move(inverted));
87 }
88}
89
90int PaintOptions::numShaderCombinations() const {
91 int numShaderCombinations = 0;
92 for (const sk_sp<PrecompileShader>& s : fShaderOptions) {
93 numShaderCombinations += s->numCombinations();
94 }
95
96 // If no shader option is specified we will add a solid color shader option
97 return numShaderCombinations ? numShaderCombinations : 1;
98}
99
100int PaintOptions::numColorFilterCombinations() const {
101 int numColorFilterCombinations = 0;
102 for (const sk_sp<PrecompileColorFilter>& cf : fColorFilterOptions) {
103 if (!cf) {
104 ++numColorFilterCombinations;
105 } else {
106 numColorFilterCombinations += cf->numCombinations();
107 }
108 }
109
110 // If no color filter options are specified we will use the unmodified result color
111 return numColorFilterCombinations ? numColorFilterCombinations : 1;
112}
113
114int PaintOptions::numBlendCombinations() const {
115 int numBlendCombos = fBlendModeOptions.size();
116 for (const sk_sp<PrecompileBlender>& b: fBlenderOptions) {
117 SkASSERT(!b->priv().asBlendMode().has_value());
118 numBlendCombos += b->priv().numChildCombinations();
119 }
120
121 if (!numBlendCombos) {
122 // If the user didn't specify a blender we will fall back to kSrcOver blending
123 numBlendCombos = 1;
124 }
125
126 return numBlendCombos;
127}
128
129int PaintOptions::numClipShaderCombinations() const {
130 int numClipShaderCombos = 0;
131 for (const sk_sp<PrecompileShader>& cs: fClipShaderOptions) {
132 if (cs) {
133 numClipShaderCombos += cs->priv().numChildCombinations();
134 } else {
135 ++numClipShaderCombos;
136 }
137 }
138
139 // If no clipShader options are specified we will just have the unclipped options
140 return numClipShaderCombos ? numClipShaderCombos : 1;
141}
142
143int PaintOptions::numCombinations() const {
144 return this->numShaderCombinations() *
145 this->numColorFilterCombinations() *
146 this->numBlendCombinations() *
147 this->numClipShaderCombinations();
148}
149
150namespace {
151
152DstReadRequirement get_dst_read_req(const Caps* caps,
154 PrecompileBlender* blender) {
155 if (blender) {
156 return GetDstReadRequirement(caps, blender->priv().asBlendMode(), coverage);
157 }
159}
160
161} // anonymous namespace
162
163void PaintOptions::createKey(const KeyContext& keyContext,
164 PaintParamsKeyBuilder* keyBuilder,
165 PipelineDataGatherer* gatherer,
166 int desiredCombination,
167 bool addPrimitiveBlender,
168 Coverage coverage) const {
169 SkDEBUGCODE(keyBuilder->checkReset();)
170 SkASSERT(desiredCombination < this->numCombinations());
171
172 const int numClipShaderCombos = this->numClipShaderCombinations();
173 const int numBlendModeCombos = this->numBlendCombinations();
174 const int numColorFilterCombinations = this->numColorFilterCombinations();
175
176 const int desiredClipShaderCombination = desiredCombination % numClipShaderCombos;
177 int remainingCombinations = desiredCombination / numClipShaderCombos;
178
179 const int desiredBlendCombination = remainingCombinations % numBlendModeCombos;
180 remainingCombinations /= numBlendModeCombos;
181
182 const int desiredColorFilterCombination = remainingCombinations % numColorFilterCombinations;
183 remainingCombinations /= numColorFilterCombinations;
184
185 const int desiredShaderCombination = remainingCombinations;
186 SkASSERT(desiredShaderCombination < this->numShaderCombinations());
187
188 // TODO: this probably needs to be passed in just like addPrimitiveBlender
189 const bool kOpaquePaintColor = true;
190
191 auto clipShader = PrecompileBase::SelectOption(SkSpan(fClipShaderOptions),
192 desiredClipShaderCombination);
193
194 std::pair<sk_sp<PrecompileBlender>, int> finalBlender;
195 if (desiredBlendCombination < fBlendModeOptions.size()) {
196 finalBlender = { PrecompileBlenders::Mode(fBlendModeOptions[desiredBlendCombination]), 0 };
197 } else {
198 finalBlender = PrecompileBase::SelectOption(
199 SkSpan(fBlenderOptions),
200 desiredBlendCombination - fBlendModeOptions.size());
201 }
202 if (!finalBlender.first) {
203 finalBlender = { PrecompileBlenders::Mode(SkBlendMode::kSrcOver), 0 };
204 }
205 DstReadRequirement dstReadReq = get_dst_read_req(keyContext.caps(), coverage,
206 finalBlender.first.get());
207
208 PaintOption option(kOpaquePaintColor,
209 finalBlender,
210 PrecompileBase::SelectOption(SkSpan(fShaderOptions),
211 desiredShaderCombination),
212 PrecompileBase::SelectOption(SkSpan(fColorFilterOptions),
213 desiredColorFilterCombination),
214 addPrimitiveBlender,
215 clipShader,
216 dstReadReq,
217 fDither);
218
219 option.toKey(keyContext, keyBuilder, gatherer);
220}
221
222namespace {
223
224void create_image_drawing_pipelines(const KeyContext& keyContext,
225 PipelineDataGatherer* gatherer,
226 const PaintOptionsPriv::ProcessCombination& processCombination,
227 const PaintOptions& orig) {
228 PaintOptions imagePaintOptions;
229
230 // For imagefilters we know we don't have alpha-only textures and don't need cubic filtering.
233
234 imagePaintOptions.setShaders({ imageShader });
235 imagePaintOptions.setBlendModes(orig.getBlendModes());
236 imagePaintOptions.setBlenders(orig.getBlenders());
237 imagePaintOptions.setColorFilters(orig.getColorFilters());
238 imagePaintOptions.priv().addColorFilter(nullptr);
239
240 imagePaintOptions.priv().buildCombinations(keyContext,
241 gatherer,
243 /* withPrimitiveBlender= */ false,
245 processCombination);
246}
247
248} // anonymous namespace
249
250void PaintOptions::buildCombinations(
251 const KeyContext& keyContext,
252 PipelineDataGatherer* gatherer,
253 DrawTypeFlags drawTypes,
254 bool withPrimitiveBlender,
256 const ProcessCombination& processCombination) const {
257
258 PaintParamsKeyBuilder builder(keyContext.dict());
259
260 if (!fImageFilterOptions.empty() || !fMaskFilterOptions.empty()) {
261 // TODO: split this out into a create_restore_draw_pipelines method
262 PaintOptions tmp = *this;
263
264 // When image filtering, the original blend mode is taken over by the restore paint
265 tmp.setImageFilters({});
266 tmp.setMaskFilters({});
267 tmp.addBlendMode(SkBlendMode::kSrcOver);
268
269 if (!fImageFilterOptions.empty()) {
270 std::vector<sk_sp<PrecompileColorFilter>> newCFs(tmp.fColorFilterOptions.begin(),
271 tmp.fColorFilterOptions.end());
272 if (newCFs.empty()) {
273 // TODO: I (robertphillips) believe this is unnecessary and is just a result of the
274 // base SkPaint generated in the PaintParamsKeyTest not correctly taking CFIFs into
275 // account.
276 newCFs.push_back(nullptr);
277 }
278
279 // As in SkCanvasPriv::ImageToColorFilter, we fuse CFIFs into the base draw's CFs.
280 // TODO: in SkCanvasPriv::ImageToColorFilter this fusing of CFIFs and CFs is skipped
281 // when there is a maskfilter. For now we over-generate.
282 for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
283 sk_sp<PrecompileColorFilter> imageFiltersCF = o ? o->asAColorFilter() : nullptr;
284 if (imageFiltersCF) {
285 if (!tmp.fColorFilterOptions.empty()) {
286 for (const sk_sp<PrecompileColorFilter>& cf : tmp.fColorFilterOptions) {
287 // TODO: if a CFIF was fully handled here it should be removed from the
288 // later loop over fImageFilterOptions. For now we over-generate.
289 sk_sp<PrecompileColorFilter> newCF = imageFiltersCF->makeComposed(cf);
290 newCFs.push_back(std::move(newCF));
291 }
292 } else {
293 newCFs.push_back(imageFiltersCF);
294 }
295 }
296 }
297
298 tmp.setColorFilters(newCFs);
299 }
300
301 tmp.buildCombinations(keyContext, gatherer, drawTypes, withPrimitiveBlender, coverage,
302 processCombination);
303
304 create_image_drawing_pipelines(keyContext, gatherer, processCombination, *this);
305
306 for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
307 o->createPipelines(keyContext, gatherer, processCombination);
308 }
309 for (const sk_sp<PrecompileMaskFilter>& o : fMaskFilterOptions) {
310 o->createPipelines(keyContext, gatherer, processCombination);
311 }
312 } else {
313 int numCombinations = this->numCombinations();
314 for (int i = 0; i < numCombinations; ++i) {
315 // Since the precompilation path's uniforms aren't used and don't change the key,
316 // the exact layout doesn't matter
317 gatherer->resetWithNewLayout(Layout::kMetal);
318
319 this->createKey(keyContext, &builder, gatherer, i, withPrimitiveBlender, coverage);
320
321 // The 'findOrCreate' calls lockAsKey on builder and then destroys the returned
322 // PaintParamsKey. This serves to reset the builder.
323 UniquePaintParamsID paintID = keyContext.dict()->findOrCreate(&builder);
324
325 processCombination(paintID, drawTypes, withPrimitiveBlender, coverage);
326 }
327 }
328}
329
331 fPaintOptions->addColorFilter(std::move(cf));
332}
333
334} // namespace skgpu::graphite
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kSrcOver
r = s + (1-sa)*d
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
constexpr T * data() const
Definition: SkSpan_impl.h:94
constexpr size_t size() const
Definition: SkSpan_impl.h:95
void addColorFilter(sk_sp< PrecompileColorFilter > cf)
PaintOptions::ProcessCombination ProcessCombination
void setColorFilters(SkSpan< const sk_sp< PrecompileColorFilter > > colorFilters)
void setBlendModes(SkSpan< const SkBlendMode > blendModes)
void setShaders(SkSpan< const sk_sp< PrecompileShader > > shaders)
void setMaskFilters(SkSpan< const sk_sp< PrecompileMaskFilter > > maskFilters)
void setImageFilters(SkSpan< const sk_sp< PrecompileImageFilter > > imageFilters)
PaintOptions & operator=(const PaintOptions &)
void setBlenders(SkSpan< const sk_sp< PrecompileBlender > > blenders)
static std::pair< sk_sp< T >, int > SelectOption(SkSpan< const sk_sp< T > > options, int desiredOption)
T * push_back_n(int n)
Definition: SkTArray.h:267
int size() const
Definition: SkTArray.h:421
static bool b
struct MyStruct s
if(end==-1)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SK_API sk_sp< PrecompileBlender > Mode(SkBlendMode)
SK_API sk_sp< PrecompileColorFilter > Blend()
sk_sp< PrecompileShader > Image(SkEnumBitMask< PrecompileImageShaderFlags >)
sk_sp< PrecompileShader > CTM(SkSpan< const sk_sp< PrecompileShader > > wrapped)
DstReadRequirement GetDstReadRequirement(const Caps *caps, std::optional< SkBlendMode > blendMode, Coverage coverage)
DstReadRequirement
Definition: Caps.h:64
Definition: ref_ptr.h:256