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
createKey(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,int desiredCombination,bool addPrimitiveBlender,Coverage coverage) const152 void PaintOptions::createKey(const KeyContext& keyContext,
153 PaintParamsKeyBuilder* keyBuilder,
154 PipelineDataGatherer* gatherer,
155 int desiredCombination,
156 bool addPrimitiveBlender,
157 Coverage coverage) const {
158 SkDEBUGCODE(keyBuilder->checkReset();)
159 SkASSERT(desiredCombination < this->numCombinations());
160
161 const int numClipShaderCombos = this->numClipShaderCombinations();
162 const int numBlendModeCombos = this->numBlendCombinations();
163 const int numColorFilterCombinations = this->numColorFilterCombinations();
164
165 const int desiredClipShaderCombination = desiredCombination % numClipShaderCombos;
166 int remainingCombinations = desiredCombination / numClipShaderCombos;
167
168 const int desiredBlendCombination = remainingCombinations % numBlendModeCombos;
169 remainingCombinations /= numBlendModeCombos;
170
171 const int desiredColorFilterCombination = remainingCombinations % numColorFilterCombinations;
172 remainingCombinations /= numColorFilterCombinations;
173
174 const int desiredShaderCombination = remainingCombinations;
175 SkASSERT(desiredShaderCombination < this->numShaderCombinations());
176
177 // TODO: this probably needs to be passed in just like addPrimitiveBlender
178 const bool kOpaquePaintColor = true;
179
180 auto clipShader = PrecompileBase::SelectOption(SkSpan(fClipShaderOptions),
181 desiredClipShaderCombination);
182
183 std::pair<sk_sp<PrecompileBlender>, int> finalBlender;
184 if (desiredBlendCombination < fBlendModeOptions.size()) {
185 finalBlender = { PrecompileBlenders::Mode(fBlendModeOptions[desiredBlendCombination]), 0 };
186 } else {
187 finalBlender = PrecompileBase::SelectOption(
188 SkSpan(fBlenderOptions),
189 desiredBlendCombination - fBlendModeOptions.size());
190 }
191 if (!finalBlender.first) {
192 finalBlender = { PrecompileBlenders::Mode(SkBlendMode::kSrcOver), 0 };
193 }
194
195 PrecompileBlender* blender = finalBlender.first.get();
196 std::optional<SkBlendMode> blendMode = blender ? blender->priv().asBlendMode()
197 : SkBlendMode::kSrcOver;
198 PaintOption option(kOpaquePaintColor,
199 finalBlender,
200 PrecompileBase::SelectOption(SkSpan(fShaderOptions),
201 desiredShaderCombination),
202 PrecompileBase::SelectOption(SkSpan(fColorFilterOptions),
203 desiredColorFilterCombination),
204 addPrimitiveBlender,
205 clipShader,
206 IsDstReadRequired(keyContext.caps(), blendMode, coverage),
207 fDither);
208
209 option.toKey(keyContext, keyBuilder, gatherer);
210 }
211
212 namespace {
213
create_image_drawing_pipelines(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const PaintOptions & orig,const RenderPassDesc & renderPassDesc,const PaintOptionsPriv::ProcessCombination & processCombination)214 void create_image_drawing_pipelines(const KeyContext& keyContext,
215 PipelineDataGatherer* gatherer,
216 const PaintOptions& orig,
217 const RenderPassDesc& renderPassDesc,
218 const PaintOptionsPriv::ProcessCombination& processCombination) {
219 PaintOptions imagePaintOptions;
220
221 // For imagefilters we know we don't have alpha-only textures and don't need cubic filtering.
222 sk_sp<PrecompileShader> imageShader = PrecompileShadersPriv::Image(
223 PrecompileImageShaderFlags::kExcludeAlpha | PrecompileImageShaderFlags::kExcludeCubic);
224
225 imagePaintOptions.setShaders({ imageShader });
226 imagePaintOptions.setBlendModes(orig.getBlendModes());
227 imagePaintOptions.setBlenders(orig.getBlenders());
228 imagePaintOptions.setColorFilters(orig.getColorFilters());
229 imagePaintOptions.priv().addColorFilter(nullptr);
230
231 imagePaintOptions.priv().buildCombinations(keyContext,
232 gatherer,
233 DrawTypeFlags::kSimpleShape,
234 /* withPrimitiveBlender= */ false,
235 Coverage::kSingleChannel,
236 renderPassDesc,
237 processCombination);
238 }
239
240 } // anonymous namespace
241
buildCombinations(const KeyContext & keyContext,PipelineDataGatherer * gatherer,DrawTypeFlags drawTypes,bool withPrimitiveBlender,Coverage coverage,const RenderPassDesc & renderPassDesc,const ProcessCombination & processCombination) const242 void PaintOptions::buildCombinations(
243 const KeyContext& keyContext,
244 PipelineDataGatherer* gatherer,
245 DrawTypeFlags drawTypes,
246 bool withPrimitiveBlender,
247 Coverage coverage,
248 const RenderPassDesc& renderPassDesc,
249 const ProcessCombination& processCombination) const {
250
251 PaintParamsKeyBuilder builder(keyContext.dict());
252
253 if (!fImageFilterOptions.empty() || !fMaskFilterOptions.empty()) {
254 // TODO: split this out into a create_restore_draw_pipelines method
255 PaintOptions tmp = *this;
256
257 // When image filtering, the original blend mode is taken over by the restore paint
258 tmp.setImageFilters({});
259 tmp.setMaskFilters({});
260 tmp.addBlendMode(SkBlendMode::kSrcOver);
261
262 if (!fImageFilterOptions.empty()) {
263 std::vector<sk_sp<PrecompileColorFilter>> newCFs(tmp.fColorFilterOptions.begin(),
264 tmp.fColorFilterOptions.end());
265 if (newCFs.empty()) {
266 // TODO: I (robertphillips) believe this is unnecessary and is just a result of the
267 // base SkPaint generated in the PaintParamsKeyTest not correctly taking CFIFs into
268 // account.
269 newCFs.push_back(nullptr);
270 }
271
272 // As in SkCanvasPriv::ImageToColorFilter, we fuse CFIFs into the base draw's CFs.
273 // TODO: in SkCanvasPriv::ImageToColorFilter this fusing of CFIFs and CFs is skipped
274 // when there is a maskfilter. For now we over-generate.
275 for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
276 sk_sp<PrecompileColorFilter> imageFiltersCF = o ? o->asAColorFilter() : nullptr;
277 if (imageFiltersCF) {
278 if (!tmp.fColorFilterOptions.empty()) {
279 for (const sk_sp<PrecompileColorFilter>& cf : tmp.fColorFilterOptions) {
280 // TODO: if a CFIF was fully handled here it should be removed from the
281 // later loop over fImageFilterOptions. For now we over-generate.
282 sk_sp<PrecompileColorFilter> newCF = imageFiltersCF->makeComposed(cf);
283 newCFs.push_back(std::move(newCF));
284 }
285 } else {
286 newCFs.push_back(imageFiltersCF);
287 }
288 }
289 }
290
291 tmp.setColorFilters(newCFs);
292 }
293
294 tmp.buildCombinations(keyContext, gatherer, drawTypes, withPrimitiveBlender, coverage,
295 renderPassDesc, processCombination);
296
297 create_image_drawing_pipelines(keyContext, gatherer, *this,
298 renderPassDesc, processCombination);
299
300 for (const sk_sp<PrecompileImageFilter>& o : fImageFilterOptions) {
301 o->createPipelines(keyContext, gatherer, renderPassDesc, processCombination);
302 }
303 for (const sk_sp<PrecompileMaskFilter>& o : fMaskFilterOptions) {
304 o->createPipelines(keyContext, gatherer, *this, renderPassDesc, processCombination);
305 }
306 } else {
307 int numCombinations = this->numCombinations();
308 for (int i = 0; i < numCombinations; ++i) {
309 // Since the precompilation path's uniforms aren't used and don't change the key,
310 // the exact layout doesn't matter
311 gatherer->resetWithNewLayout(Layout::kMetal);
312
313 this->createKey(keyContext, &builder, gatherer, i, withPrimitiveBlender, coverage);
314
315 // The 'findOrCreate' calls lockAsKey on builder and then destroys the returned
316 // PaintParamsKey. This serves to reset the builder.
317 UniquePaintParamsID paintID = keyContext.dict()->findOrCreate(&builder);
318
319 processCombination(paintID, drawTypes, withPrimitiveBlender, coverage, renderPassDesc);
320 }
321 }
322 }
323
addColorFilter(sk_sp<PrecompileColorFilter> cf)324 void PaintOptionsPriv::addColorFilter(sk_sp<PrecompileColorFilter> cf) {
325 fPaintOptions->addColorFilter(std::move(cf));
326 }
327
328 } // namespace skgpu::graphite
329