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