1 /*
2 * Copyright 2015 Google Inc.
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 "src/gpu/ganesh/effects/GrCustomXfermode.h"
9
10 #include "src/gpu/KeyBuilder.h"
11 #include "src/gpu/ganesh/GrCaps.h"
12 #include "src/gpu/ganesh/GrFragmentProcessor.h"
13 #include "src/gpu/ganesh/GrPipeline.h"
14 #include "src/gpu/ganesh/GrProcessor.h"
15 #include "src/gpu/ganesh/GrProcessorUnitTest.h"
16 #include "src/gpu/ganesh/GrShaderCaps.h"
17 #include "src/gpu/ganesh/GrXferProcessor.h"
18 #include "src/gpu/ganesh/glsl/GrGLSLBlend.h"
19 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
21 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
22
IsSupportedMode(SkBlendMode mode)23 bool GrCustomXfermode::IsSupportedMode(SkBlendMode mode) {
24 return (int)mode > (int)SkBlendMode::kLastCoeffMode &&
25 (int)mode <= (int)SkBlendMode::kLastMode;
26 }
27
28 ///////////////////////////////////////////////////////////////////////////////
29 // Static helpers
30 ///////////////////////////////////////////////////////////////////////////////
31
hw_blend_equation(SkBlendMode mode)32 static constexpr skgpu::BlendEquation hw_blend_equation(SkBlendMode mode) {
33 constexpr int kEqOffset = ((int)skgpu::BlendEquation::kOverlay - (int)SkBlendMode::kOverlay);
34 static_assert((int)skgpu::BlendEquation::kOverlay == (int)SkBlendMode::kOverlay + kEqOffset);
35 static_assert((int)skgpu::BlendEquation::kDarken == (int)SkBlendMode::kDarken + kEqOffset);
36 static_assert((int)skgpu::BlendEquation::kLighten == (int)SkBlendMode::kLighten + kEqOffset);
37 static_assert((int)skgpu::BlendEquation::kColorDodge == (int)SkBlendMode::kColorDodge + kEqOffset);
38 static_assert((int)skgpu::BlendEquation::kColorBurn == (int)SkBlendMode::kColorBurn + kEqOffset);
39 static_assert((int)skgpu::BlendEquation::kHardLight == (int)SkBlendMode::kHardLight + kEqOffset);
40 static_assert((int)skgpu::BlendEquation::kSoftLight == (int)SkBlendMode::kSoftLight + kEqOffset);
41 static_assert((int)skgpu::BlendEquation::kDifference == (int)SkBlendMode::kDifference + kEqOffset);
42 static_assert((int)skgpu::BlendEquation::kExclusion == (int)SkBlendMode::kExclusion + kEqOffset);
43 static_assert((int)skgpu::BlendEquation::kMultiply == (int)SkBlendMode::kMultiply + kEqOffset);
44 static_assert((int)skgpu::BlendEquation::kHSLHue == (int)SkBlendMode::kHue + kEqOffset);
45 static_assert((int)skgpu::BlendEquation::kHSLSaturation == (int)SkBlendMode::kSaturation + kEqOffset);
46 static_assert((int)skgpu::BlendEquation::kHSLColor == (int)SkBlendMode::kColor + kEqOffset);
47 static_assert((int)skgpu::BlendEquation::kHSLLuminosity == (int)SkBlendMode::kLuminosity + kEqOffset);
48
49 // There's an illegal BlendEquation that corresponds to no SkBlendMode, hence the extra +1.
50 static_assert(skgpu::kBlendEquationCnt == (int)SkBlendMode::kLastMode + 1 + 1 + kEqOffset);
51
52 return static_cast<skgpu::BlendEquation>((int)mode + kEqOffset);
53 #undef EQ_OFFSET
54 }
55
can_use_hw_blend_equation(skgpu::BlendEquation equation,GrProcessorAnalysisCoverage coverage,const GrCaps & caps)56 static bool can_use_hw_blend_equation(skgpu::BlendEquation equation,
57 GrProcessorAnalysisCoverage coverage, const GrCaps& caps) {
58 if (!caps.advancedBlendEquationSupport()) {
59 return false;
60 }
61 if (GrProcessorAnalysisCoverage::kLCD == coverage) {
62 return false; // LCD coverage must be applied after the blend equation.
63 }
64 if (caps.isAdvancedBlendEquationDisabled(equation)) {
65 return false;
66 }
67 return true;
68 }
69
70 ///////////////////////////////////////////////////////////////////////////////
71 // Xfer Processor
72 ///////////////////////////////////////////////////////////////////////////////
73
74 class CustomXP : public GrXferProcessor {
75 public:
CustomXP(SkBlendMode mode,skgpu::BlendEquation hwBlendEquation)76 CustomXP(SkBlendMode mode, skgpu::BlendEquation hwBlendEquation)
77 : INHERITED(kCustomXP_ClassID)
78 , fMode(mode)
79 , fHWBlendEquation(hwBlendEquation) {}
80
CustomXP(SkBlendMode mode,GrProcessorAnalysisCoverage coverage)81 CustomXP(SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
82 : INHERITED(kCustomXP_ClassID, /*willReadDstColor=*/true, coverage)
83 , fMode(mode)
84 , fHWBlendEquation(skgpu::BlendEquation::kIllegal) {
85 }
86
name() const87 const char* name() const override { return "Custom Xfermode"; }
88
89 std::unique_ptr<ProgramImpl> makeProgramImpl() const override;
90
91 GrXferBarrierType xferBarrierType(const GrCaps&) const override;
92
93 private:
hasHWBlendEquation() const94 bool hasHWBlendEquation() const { return skgpu::BlendEquation::kIllegal != fHWBlendEquation; }
95
96 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override;
97
98 void onGetBlendInfo(skgpu::BlendInfo*) const override;
99
100 bool onIsEqual(const GrXferProcessor& xpBase) const override;
101
102 const SkBlendMode fMode;
103 const skgpu::BlendEquation fHWBlendEquation;
104
105 using INHERITED = GrXferProcessor;
106 };
107
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const108 void CustomXP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
109 if (this->hasHWBlendEquation()) {
110 SkASSERT(caps.fAdvBlendEqInteraction > 0); // 0 will mean !xp.hasHWBlendEquation().
111 b->addBool(true, "has hardware blend equation");
112 b->add32(caps.fAdvBlendEqInteraction);
113 } else {
114 b->addBool(false, "has hardware blend equation");
115 b->add32(GrGLSLBlend::BlendKey(fMode));
116 }
117 }
118
makeProgramImpl() const119 std::unique_ptr<GrXferProcessor::ProgramImpl> CustomXP::makeProgramImpl() const {
120 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
121
122 class Impl : public ProgramImpl {
123 private:
124 void emitOutputsForBlendState(const EmitArgs& args) override {
125 const CustomXP& xp = args.fXP.cast<CustomXP>();
126 SkASSERT(xp.hasHWBlendEquation());
127
128 GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
129 fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.fHWBlendEquation);
130
131 // Apply coverage by multiplying it into the src color before blending. This will "just
132 // work" automatically. (See analysisProperties())
133 fragBuilder->codeAppendf("%s = %s * %s;",
134 args.fOutputPrimary,
135 args.fInputCoverage,
136 args.fInputColor);
137 }
138
139 void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
140 GrGLSLUniformHandler* uniformHandler,
141 const char* srcColor,
142 const char* srcCoverage,
143 const char* dstColor,
144 const char* outColor,
145 const char* outColorSecondary,
146 const GrXferProcessor& proc) override {
147 const CustomXP& xp = proc.cast<CustomXP>();
148 SkASSERT(!xp.hasHWBlendEquation());
149
150 std::string blendExpr = GrGLSLBlend::BlendExpression(
151 &xp, uniformHandler, &fBlendUniform, srcColor, dstColor, xp.fMode);
152 fragBuilder->codeAppendf("%s = %s;", outColor, blendExpr.c_str());
153
154 // Apply coverage.
155 DefaultCoverageModulation(fragBuilder,
156 srcCoverage,
157 dstColor,
158 outColor,
159 outColorSecondary,
160 xp);
161 }
162
163 void onSetData(const GrGLSLProgramDataManager& pdman,
164 const GrXferProcessor& proc) override {
165 if (fBlendUniform.isValid()) {
166 const CustomXP& xp = proc.cast<CustomXP>();
167 GrGLSLBlend::SetBlendModeUniformData(pdman, fBlendUniform, xp.fMode);
168 }
169 }
170
171 GrGLSLUniformHandler::UniformHandle fBlendUniform;
172 };
173
174 return std::make_unique<Impl>();
175 }
176
onIsEqual(const GrXferProcessor & other) const177 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
178 const CustomXP& s = other.cast<CustomXP>();
179 return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
180 }
181
xferBarrierType(const GrCaps & caps) const182 GrXferBarrierType CustomXP::xferBarrierType(const GrCaps& caps) const {
183 if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
184 return kBlend_GrXferBarrierType;
185 }
186 return kNone_GrXferBarrierType;
187 }
188
onGetBlendInfo(skgpu::BlendInfo * blendInfo) const189 void CustomXP::onGetBlendInfo(skgpu::BlendInfo* blendInfo) const {
190 if (this->hasHWBlendEquation()) {
191 blendInfo->fEquation = fHWBlendEquation;
192 }
193 }
194
195 ///////////////////////////////////////////////////////////////////////////////
196
197 // See the comment above GrXPFactory's definition about this warning suppression.
198 #if defined(__GNUC__)
199 #pragma GCC diagnostic push
200 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
201 #endif
202 #if defined(__clang__)
203 #pragma clang diagnostic push
204 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
205 #endif
206 class CustomXPFactory : public GrXPFactory {
207 public:
CustomXPFactory(SkBlendMode mode)208 constexpr CustomXPFactory(SkBlendMode mode)
209 : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
210
211 private:
212 sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
213 GrProcessorAnalysisCoverage,
214 const GrCaps&,
215 GrClampType) const override;
216
217 AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
218 const GrProcessorAnalysisCoverage&,
219 const GrCaps&,
220 GrClampType) const override;
221
222 GR_DECLARE_XP_FACTORY_TEST
223
224 SkBlendMode fMode;
225 skgpu::BlendEquation fHWBlendEquation;
226
227 using INHERITED = GrXPFactory;
228 };
229 #if defined(__GNUC__)
230 #pragma GCC diagnostic pop
231 #endif
232 #if defined(__clang__)
233 #pragma clang diagnostic pop
234 #endif
235
makeXferProcessor(const GrProcessorAnalysisColor &,GrProcessorAnalysisCoverage coverage,const GrCaps & caps,GrClampType clampType) const236 sk_sp<const GrXferProcessor> CustomXPFactory::makeXferProcessor(
237 const GrProcessorAnalysisColor&,
238 GrProcessorAnalysisCoverage coverage,
239 const GrCaps& caps,
240 GrClampType clampType) const {
241 SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
242 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
243 return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
244 }
245 return sk_sp<GrXferProcessor>(new CustomXP(fMode, coverage));
246 }
247
analysisProperties(const GrProcessorAnalysisColor &,const GrProcessorAnalysisCoverage & coverage,const GrCaps & caps,GrClampType clampType) const248 GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(
249 const GrProcessorAnalysisColor&, const GrProcessorAnalysisCoverage& coverage,
250 const GrCaps& caps, GrClampType clampType) const {
251 /*
252 The general SVG blend equation is defined in the spec as follows:
253
254 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
255 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
256
257 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
258 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
259 RGB colors.)
260
261 For every blend mode supported by this class, i.e. the "advanced" blend
262 modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
263
264 It can be shown that when X=Y=Z=1, these equations can modulate alpha for
265 coverage.
266
267
268 == Color ==
269
270 We substitute Y=Z=1 and define a blend() function that calculates Dca' in
271 terms of premultiplied alpha only:
272
273 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
274 Sca : if Da == 0,
275 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if
276 Sa,Da != 0}
277
278 And for coverage modulation, we use a post blend src-over model:
279
280 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
281
282 (Where f is the fractional coverage.)
283
284 Next we show that canTweakAlphaForCoverage() is true by proving the
285 following relationship:
286
287 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
288
289 General case (f,Sa,Da != 0):
290
291 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
292 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da !=
293 0, definition of blend()]
294 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
295 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
296 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
297 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
298 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
299 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
300 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
301
302 Corner cases (Sa=0, Da=0, and f=0):
303
304 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
305 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
306 = Dca
307 = blend(0, Dca, 0, Da) [definition of blend()]
308 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
309
310 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
311 = f * Sca + (1-f) * Dca [Da=0, definition of blend()]
312 = f * Sca [Da=0]
313 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
314 = blend(f*Sca, Dca, f*Sa, Da) [Da=0]
315
316 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
317 = Dca [f=0]
318 = blend(0, Dca, 0, Da) [definition of blend()]
319 = blend(f*Sca, Dca, f*Sa, Da) [f=0]
320
321 == Alpha ==
322
323 We substitute X=Y=Z=1 and define a blend() function that calculates Da':
324
325 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
326 = Sa * Da + Sa - Sa * Da + Da - Da * Sa
327 = Sa + Da - Sa * Da
328
329 We use the same model for coverage modulation as we did with color:
330
331 Da'' = f * blend(Sa, Da) + (1-f) * Da
332
333 And show that canTweakAlphaForCoverage() is true by proving the following
334 relationship:
335
336 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
337
338
339 f * blend(Sa, Da) + (1-f) * Da
340 = f * (Sa + Da - Sa * Da) + (1-f) * Da
341 = f*Sa + f*Da - f*Sa * Da + Da - f*Da
342 = f*Sa - f*Sa * Da + Da
343 = f*Sa + Da - f*Sa * Da
344 = blend(f*Sa, Da)
345 */
346 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
347 if (caps.blendEquationSupport() == GrCaps::kAdvancedCoherent_BlendEquationSupport) {
348 return AnalysisProperties::kCompatibleWithCoverageAsAlpha;
349 } else {
350 return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
351 AnalysisProperties::kRequiresNonOverlappingDraws |
352 AnalysisProperties::kUsesNonCoherentHWBlending;
353 }
354 }
355 return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
356 AnalysisProperties::kReadsDstInShader;
357 }
358
GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory) const359 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory)
360 #if GR_TEST_UTILS
361 const GrXPFactory* CustomXPFactory::TestGet(GrProcessorTestData* d) {
362 int mode = d->fRandom->nextRangeU((int)SkBlendMode::kLastCoeffMode + 1,
363 (int)SkBlendMode::kLastSeparableMode);
364
365 return GrCustomXfermode::Get((SkBlendMode)mode);
366 }
367 #endif
368
369 ///////////////////////////////////////////////////////////////////////////////
370
Get(SkBlendMode mode)371 const GrXPFactory* GrCustomXfermode::Get(SkBlendMode mode) {
372 static constexpr const CustomXPFactory gOverlay(SkBlendMode::kOverlay);
373 static constexpr const CustomXPFactory gDarken(SkBlendMode::kDarken);
374 static constexpr const CustomXPFactory gLighten(SkBlendMode::kLighten);
375 static constexpr const CustomXPFactory gColorDodge(SkBlendMode::kColorDodge);
376 static constexpr const CustomXPFactory gColorBurn(SkBlendMode::kColorBurn);
377 static constexpr const CustomXPFactory gHardLight(SkBlendMode::kHardLight);
378 static constexpr const CustomXPFactory gSoftLight(SkBlendMode::kSoftLight);
379 static constexpr const CustomXPFactory gDifference(SkBlendMode::kDifference);
380 static constexpr const CustomXPFactory gExclusion(SkBlendMode::kExclusion);
381 static constexpr const CustomXPFactory gMultiply(SkBlendMode::kMultiply);
382 static constexpr const CustomXPFactory gHue(SkBlendMode::kHue);
383 static constexpr const CustomXPFactory gSaturation(SkBlendMode::kSaturation);
384 static constexpr const CustomXPFactory gColor(SkBlendMode::kColor);
385 static constexpr const CustomXPFactory gLuminosity(SkBlendMode::kLuminosity);
386 switch (mode) {
387 case SkBlendMode::kOverlay:
388 return &gOverlay;
389 case SkBlendMode::kDarken:
390 return &gDarken;
391 case SkBlendMode::kLighten:
392 return &gLighten;
393 case SkBlendMode::kColorDodge:
394 return &gColorDodge;
395 case SkBlendMode::kColorBurn:
396 return &gColorBurn;
397 case SkBlendMode::kHardLight:
398 return &gHardLight;
399 case SkBlendMode::kSoftLight:
400 return &gSoftLight;
401 case SkBlendMode::kDifference:
402 return &gDifference;
403 case SkBlendMode::kExclusion:
404 return &gExclusion;
405 case SkBlendMode::kMultiply:
406 return &gMultiply;
407 case SkBlendMode::kHue:
408 return &gHue;
409 case SkBlendMode::kSaturation:
410 return &gSaturation;
411 case SkBlendMode::kColor:
412 return &gColor;
413 case SkBlendMode::kLuminosity:
414 return &gLuminosity;
415 default:
416 SkASSERT(!GrCustomXfermode::IsSupportedMode(mode));
417 return nullptr;
418 }
419 }
420