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