• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "include/gpu/graphite/precompile/PaintOptions.h"
9 
10 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
11 #include "include/gpu/graphite/precompile/PrecompileShader.h"
12 #include "src/gpu/graphite/Caps.h"
13 #include "src/gpu/graphite/ContextUtils.h"
14 #include "src/gpu/graphite/FactoryFunctionsPriv.h"
15 #include "src/gpu/graphite/KeyContext.h"
16 #include "src/gpu/graphite/PaintParamsKey.h"
17 #include "src/gpu/graphite/PrecompileInternal.h"
18 #include "src/gpu/graphite/Renderer.h"
19 #include "src/gpu/graphite/ShaderCodeDictionary.h"
20 #include "src/gpu/graphite/precompile/PaintOption.h"
21 #include "src/gpu/graphite/precompile/PaintOptionsPriv.h"
22 #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h"
23 #include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
24 #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h"
25 #include "src/gpu/graphite/precompile/PrecompileShaderPriv.h"
26 
27 namespace skgpu::graphite {
28 
29 PaintOptions::PaintOptions() = default;
30 PaintOptions::PaintOptions(const PaintOptions&) = default;
31 PaintOptions::~PaintOptions() = default;
32 PaintOptions& PaintOptions::operator=(const PaintOptions&) = default;
33 
setShaders(SkSpan<const sk_sp<PrecompileShader>> shaders)34 void PaintOptions::setShaders(SkSpan<const sk_sp<PrecompileShader>> shaders) {
35     fShaderOptions.clear();
36     fShaderOptions.push_back_n(shaders.size(), shaders.data());
37 }
38 
setImageFilters(SkSpan<const sk_sp<PrecompileImageFilter>> imageFilters)39 void PaintOptions::setImageFilters(SkSpan<const sk_sp<PrecompileImageFilter>> imageFilters) {
40     fImageFilterOptions.clear();
41     fImageFilterOptions.push_back_n(imageFilters.size(), imageFilters.data());
42 }
43 
setMaskFilters(SkSpan<const sk_sp<PrecompileMaskFilter>> maskFilters)44 void PaintOptions::setMaskFilters(SkSpan<const sk_sp<PrecompileMaskFilter>> maskFilters) {
45     fMaskFilterOptions.clear();
46     fMaskFilterOptions.push_back_n(maskFilters.size(), maskFilters.data());
47 }
48 
setColorFilters(SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)49 void PaintOptions::setColorFilters(SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters) {
50     fColorFilterOptions.clear();
51     fColorFilterOptions.push_back_n(colorFilters.size(), colorFilters.data());
52 }
53 
addColorFilter(sk_sp<PrecompileColorFilter> cf)54 void PaintOptions::addColorFilter(sk_sp<PrecompileColorFilter> cf) {
55     fColorFilterOptions.push_back(std::move(cf));
56 }
57 
setBlendModes(SkSpan<const SkBlendMode> blendModes)58 void PaintOptions::setBlendModes(SkSpan<const SkBlendMode> blendModes) {
59     fBlendModeOptions.clear();
60     fBlendModeOptions.push_back_n(blendModes.size(), blendModes.data());
61 }
62 
setBlenders(SkSpan<const sk_sp<PrecompileBlender>> blenders)63 void PaintOptions::setBlenders(SkSpan<const sk_sp<PrecompileBlender>> blenders) {
64     for (const sk_sp<PrecompileBlender>& b: blenders) {
65         if (b->priv().asBlendMode().has_value()) {
66             fBlendModeOptions.push_back(b->priv().asBlendMode().value());
67         } else {
68             fBlenderOptions.push_back(b);
69         }
70     }
71 }
72 
setClipShaders(SkSpan<const sk_sp<PrecompileShader>> clipShaders)73 void PaintOptions::setClipShaders(SkSpan<const sk_sp<PrecompileShader>> clipShaders) {
74     // In the normal API this modification happens in SkDevice::clipShader()
75     fClipShaderOptions.reserve(2 * clipShaders.size());
76     for (const sk_sp<PrecompileShader>& cs : clipShaders) {
77         // All clipShaders get wrapped in a CTMShader ...
78         sk_sp<PrecompileShader> withCTM = cs ? PrecompileShadersPriv::CTM({ cs }) : nullptr;
79         // and, if it is a SkClipOp::kDifference clip, an additional ColorFilterShader
80         sk_sp<PrecompileShader> inverted =
81                 withCTM ? withCTM->makeWithColorFilter(PrecompileColorFilters::Blend())
82                         : nullptr;
83 
84         fClipShaderOptions.emplace_back(std::move(withCTM));
85         fClipShaderOptions.emplace_back(std::move(inverted));
86     }
87 }
88 
numShaderCombinations() const89 int PaintOptions::numShaderCombinations() const {
90     int numShaderCombinations = 0;
91     for (const sk_sp<PrecompileShader>& s : fShaderOptions) {
92         numShaderCombinations += s->numCombinations();
93     }
94 
95     // If no shader option is specified we will add a solid color shader option
96     return numShaderCombinations ? numShaderCombinations : 1;
97 }
98 
numColorFilterCombinations() const99 int PaintOptions::numColorFilterCombinations() const {
100     int numColorFilterCombinations = 0;
101     for (const sk_sp<PrecompileColorFilter>& cf : fColorFilterOptions) {
102         if (!cf) {
103             ++numColorFilterCombinations;
104         } else {
105             numColorFilterCombinations += cf->numCombinations();
106         }
107     }
108 
109     // If no color filter options are specified we will use the unmodified result color
110     return numColorFilterCombinations ? numColorFilterCombinations : 1;
111 }
112 
numBlendCombinations() const113 int PaintOptions::numBlendCombinations() const {
114     int numBlendCombos = fBlendModeOptions.size();
115     for (const sk_sp<PrecompileBlender>& b: fBlenderOptions) {
116         SkASSERT(!b->priv().asBlendMode().has_value());
117         numBlendCombos += b->priv().numChildCombinations();
118     }
119 
120     if (!numBlendCombos) {
121         // If the user didn't specify a blender we will fall back to kSrcOver blending
122         numBlendCombos = 1;
123     }
124 
125     return numBlendCombos;
126 }
127 
numClipShaderCombinations() const128 int PaintOptions::numClipShaderCombinations() const {
129     int numClipShaderCombos = 0;
130     for (const sk_sp<PrecompileShader>& cs: fClipShaderOptions) {
131         if (cs) {
132             numClipShaderCombos += cs->priv().numChildCombinations();
133         } else {
134             ++numClipShaderCombos;
135         }
136     }
137 
138     // If no clipShader options are specified we will just have the unclipped options
139     return numClipShaderCombos ? numClipShaderCombos : 1;
140 }
141 
numCombinations() const142 int PaintOptions::numCombinations() const {
143     return this->numShaderCombinations() *
144            this->numColorFilterCombinations() *
145            this->numBlendCombinations() *
146            this->numClipShaderCombinations();
147 }
148 
149 namespace {
150 
get_dst_read_req(const Caps * caps,Coverage coverage,PrecompileBlender * blender)151 DstReadRequirement get_dst_read_req(const Caps* caps,
152                                     Coverage coverage,
153                                     PrecompileBlender* blender) {
154     if (blender) {
155         return GetDstReadRequirement(caps, blender->priv().asBlendMode(), coverage);
156     }
157     return GetDstReadRequirement(caps, SkBlendMode::kSrcOver, coverage);
158 }
159 
160 } // anonymous namespace
161 
createKey(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,int desiredCombination,bool addPrimitiveBlender,Coverage coverage) const162 void PaintOptions::createKey(const KeyContext& keyContext,
163                              PaintParamsKeyBuilder* keyBuilder,
164                              PipelineDataGatherer* gatherer,
165                              int desiredCombination,
166                              bool addPrimitiveBlender,
167                              Coverage coverage) const {
168     SkDEBUGCODE(keyBuilder->checkReset();)
169     SkASSERT(desiredCombination < this->numCombinations());
170 
171     const int numClipShaderCombos = this->numClipShaderCombinations();
172     const int numBlendModeCombos = this->numBlendCombinations();
173     const int numColorFilterCombinations = this->numColorFilterCombinations();
174 
175     const int desiredClipShaderCombination = desiredCombination % numClipShaderCombos;
176     int remainingCombinations = desiredCombination / numClipShaderCombos;
177 
178     const int desiredBlendCombination = remainingCombinations % numBlendModeCombos;
179     remainingCombinations /= numBlendModeCombos;
180 
181     const int desiredColorFilterCombination = remainingCombinations % numColorFilterCombinations;
182     remainingCombinations /= numColorFilterCombinations;
183 
184     const int desiredShaderCombination = remainingCombinations;
185     SkASSERT(desiredShaderCombination < this->numShaderCombinations());
186 
187     // TODO: this probably needs to be passed in just like addPrimitiveBlender
188     const bool kOpaquePaintColor = true;
189 
190     auto clipShader = PrecompileBase::SelectOption(SkSpan(fClipShaderOptions),
191                                                    desiredClipShaderCombination);
192 
193     std::pair<sk_sp<PrecompileBlender>, int> finalBlender;
194     if (desiredBlendCombination < fBlendModeOptions.size()) {
195         finalBlender = { PrecompileBlenders::Mode(fBlendModeOptions[desiredBlendCombination]), 0 };
196     } else {
197         finalBlender = PrecompileBase::SelectOption(
198                             SkSpan(fBlenderOptions),
199                             desiredBlendCombination - fBlendModeOptions.size());
200     }
201     if (!finalBlender.first) {
202         finalBlender = { PrecompileBlenders::Mode(SkBlendMode::kSrcOver), 0 };
203     }
204     DstReadRequirement dstReadReq = get_dst_read_req(keyContext.caps(), coverage,
205                                                      finalBlender.first.get());
206 
207     PaintOption option(kOpaquePaintColor,
208                        finalBlender,
209                        PrecompileBase::SelectOption(SkSpan(fShaderOptions),
210                                                     desiredShaderCombination),
211                        PrecompileBase::SelectOption(SkSpan(fColorFilterOptions),
212                                                     desiredColorFilterCombination),
213                        addPrimitiveBlender,
214                        clipShader,
215                        dstReadReq,
216                        fDither);
217 
218     option.toKey(keyContext, keyBuilder, gatherer);
219 }
220 
221 namespace {
222 
create_image_drawing_pipelines(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const PaintOptionsPriv::ProcessCombination & processCombination,const PaintOptions & orig)223 void create_image_drawing_pipelines(const KeyContext& keyContext,
224                                     PipelineDataGatherer* gatherer,
225                                     const PaintOptionsPriv::ProcessCombination& processCombination,
226                                     const PaintOptions& orig) {
227     PaintOptions imagePaintOptions;
228 
229     // For imagefilters we know we don't have alpha-only textures and don't need cubic filtering.
230     sk_sp<PrecompileShader> imageShader = PrecompileShadersPriv::Image(
231             PrecompileImageShaderFlags::kExcludeAlpha | PrecompileImageShaderFlags::kExcludeCubic);
232 
233     imagePaintOptions.setShaders({ imageShader });
234     imagePaintOptions.setBlendModes(orig.getBlendModes());
235     imagePaintOptions.setBlenders(orig.getBlenders());
236     imagePaintOptions.setColorFilters(orig.getColorFilters());
237     imagePaintOptions.priv().addColorFilter(nullptr);
238 
239     imagePaintOptions.priv().buildCombinations(keyContext,
240                                                gatherer,
241                                                DrawTypeFlags::kSimpleShape,
242                                                /* withPrimitiveBlender= */ false,
243                                                Coverage::kSingleChannel,
244                                                processCombination);
245 }
246 
247 } // anonymous namespace
248 
buildCombinations(const KeyContext & keyContext,PipelineDataGatherer * gatherer,DrawTypeFlags drawTypes,bool withPrimitiveBlender,Coverage coverage,const ProcessCombination & processCombination) const249 void PaintOptions::buildCombinations(
250         const KeyContext& keyContext,
251         PipelineDataGatherer* gatherer,
252         DrawTypeFlags drawTypes,
253         bool withPrimitiveBlender,
254         Coverage coverage,
255         const ProcessCombination& processCombination) const {
256 
257     PaintParamsKeyBuilder builder(keyContext.dict());
258 
259     if (!fImageFilterOptions.empty() || !fMaskFilterOptions.empty()) {
260         // TODO: split this out into a create_restore_draw_pipelines method
261         PaintOptions tmp = *this;
262 
263         // When image filtering, the original blend mode is taken over by the restore paint
264         tmp.setImageFilters({});
265         tmp.setMaskFilters({});
266         tmp.addBlendMode(SkBlendMode::kSrcOver);
267 
268         if (!fImageFilterOptions.empty()) {
269             std::vector<sk_sp<PrecompileColorFilter>> newCFs(tmp.fColorFilterOptions.begin(),
270                                                              tmp.fColorFilterOptions.end());
271             if (newCFs.empty()) {
272                 // TODO: I (robertphillips) believe this is unnecessary and is just a result of the
273                 // base SkPaint generated in the PaintParamsKeyTest not correctly taking CFIFs into
274                 // account.
275                 newCFs.push_back(nullptr);
276             }
277 
278             // As in SkCanvasPriv::ImageToColorFilter, we fuse CFIFs into the base draw's CFs.
279             // TODO: in SkCanvasPriv::ImageToColorFilter this fusing of CFIFs and CFs is skipped
280             // when there is a maskfilter. For now we over-generate.
281             for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
282                 sk_sp<PrecompileColorFilter> imageFiltersCF = o ? o->asAColorFilter() : nullptr;
283                 if (imageFiltersCF) {
284                     if (!tmp.fColorFilterOptions.empty()) {
285                         for (const sk_sp<PrecompileColorFilter>& cf : tmp.fColorFilterOptions) {
286                             // TODO: if a CFIF was fully handled here it should be removed from the
287                             // later loop over fImageFilterOptions. For now we over-generate.
288                             sk_sp<PrecompileColorFilter> newCF = imageFiltersCF->makeComposed(cf);
289                             newCFs.push_back(std::move(newCF));
290                         }
291                     } else {
292                         newCFs.push_back(imageFiltersCF);
293                     }
294                 }
295             }
296 
297             tmp.setColorFilters(newCFs);
298         }
299 
300         tmp.buildCombinations(keyContext, gatherer, drawTypes, withPrimitiveBlender, coverage,
301                               processCombination);
302 
303         create_image_drawing_pipelines(keyContext, gatherer, processCombination, *this);
304 
305         for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
306             o->createPipelines(keyContext, gatherer, processCombination);
307         }
308         for (const sk_sp<PrecompileMaskFilter>& o : fMaskFilterOptions) {
309             o->createPipelines(keyContext, gatherer, processCombination);
310         }
311     } else {
312         int numCombinations = this->numCombinations();
313         for (int i = 0; i < numCombinations; ++i) {
314             // Since the precompilation path's uniforms aren't used and don't change the key,
315             // the exact layout doesn't matter
316             gatherer->resetWithNewLayout(Layout::kMetal);
317 
318             this->createKey(keyContext, &builder, gatherer, i, withPrimitiveBlender, coverage);
319 
320             // The 'findOrCreate' calls lockAsKey on builder and then destroys the returned
321             // PaintParamsKey. This serves to reset the builder.
322             UniquePaintParamsID paintID = keyContext.dict()->findOrCreate(&builder);
323 
324             processCombination(paintID, drawTypes, withPrimitiveBlender, coverage);
325         }
326     }
327 }
328 
329 } // namespace skgpu::graphite
330