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