OLD | NEW |
---|---|
(Empty) | |
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 "effects/GrAdvancedEquationXferProcessor.h" | |
9 | |
10 #include "GrDrawTargetCaps.h" | |
11 #include "GrGpu.h" | |
12 #include "gl/GrGLXferProcessor.h" | |
13 #include "gl/builders/GrGLFragmentShaderBuilder.h" | |
14 #include "gl/builders/GrGLProgramBuilder.h" | |
15 | |
16 class AdvancedEquationXPBase : public GrXferProcessor { | |
17 public: | |
18 static AdvancedEquationXPBase* Create(bool hasCoverage, GrBlendEquation equa tion); | |
19 AdvancedEquationXPBase(GrBlendEquation equation) : fEquation(equation) {} | |
20 | |
21 const char* name() const override { return "Blend Equation Advanced"; } | |
22 bool hasSecondaryOutput() const override { return false; } | |
23 | |
24 bool willNeedXferBarrier(const GrRenderTarget* rt, const GrDrawTargetCaps&, | |
25 GrXferBarrierType* outBarrierType) const override; | |
26 | |
27 protected: | |
28 void onGetGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const over ride; | |
29 | |
30 void onGetBlendInfo(BlendInfo*) const override; | |
31 | |
32 bool onIsEqual(const GrXferProcessor& xpBase) const override { | |
33 const AdvancedEquationXPBase& xp = xpBase.cast<AdvancedEquationXPBase>() ; | |
34 return fEquation == xp.fEquation; | |
35 } | |
36 | |
37 const GrBlendEquation fEquation; | |
38 | |
39 typedef GrXferProcessor INHERITED; | |
40 }; | |
41 | |
42 template<bool HasCoverage> class AdvancedEquationXP: public AdvancedEquationXPBa se { | |
43 public: | |
44 AdvancedEquationXP(GrBlendEquation equation) : INHERITED(equation) { | |
45 this->initClassID<AdvancedEquationXP>(); | |
46 } | |
47 | |
48 OptFlags getOptimizations(const GrProcOptInfo&, const GrProcOptInfo&, bool, GrColor*, | |
49 const GrDrawTargetCaps&) override; | |
50 | |
51 GrGLXferProcessor* createGLInstance() const override; | |
52 | |
53 private: | |
54 typedef AdvancedEquationXPBase INHERITED; | |
55 }; | |
56 | |
57 | |
58 bool GrAdvancedEquationXPFactory::IsSupported(const GrContext* context, SkXfermo de::Mode mode) { | |
59 if (!context->getGpu()->caps()->advancedBlendEquationSupport()) { | |
60 return false; | |
61 } | |
62 if (mode > SkXfermode::kLastMode) { | |
63 return false; | |
64 } | |
65 return mode == SkXfermode::kScreen_Mode || mode > SkXfermode::kLastCoeffMode ; | |
66 } | |
67 | |
68 GrXPFactory* GrAdvancedEquationXPFactory::Create(SkXfermode::Mode mode) { | |
69 switch (mode) { | |
70 default: break; | |
71 case SkXfermode::kScreen_Mode: return RefFactory<kScreen_GrBlendEquation >(); | |
72 case SkXfermode::kOverlay_Mode: return RefFactory<kOverlay_GrBlendEquati on>(); | |
73 case SkXfermode::kDarken_Mode: return RefFactory<kDarken_GrBlendEquation >(); | |
74 case SkXfermode::kLighten_Mode: return RefFactory<kLighten_GrBlendEquati on>(); | |
75 case SkXfermode::kColorDodge_Mode: return RefFactory<kColorDodge_GrBlend Equation>(); | |
76 case SkXfermode::kColorBurn_Mode: return RefFactory<kColorBurn_GrBlendEq uation>(); | |
77 case SkXfermode::kHardLight_Mode: return RefFactory<kHardLight_GrBlendEq uation>(); | |
78 case SkXfermode::kSoftLight_Mode: return RefFactory<kSoftLight_GrBlendEq uation>(); | |
79 case SkXfermode::kDifference_Mode: return RefFactory<kDifference_GrBlend Equation>(); | |
80 case SkXfermode::kExclusion_Mode: return RefFactory<kExclusion_GrBlendEq uation>(); | |
81 case SkXfermode::kMultiply_Mode: return RefFactory<kMultiply_GrBlendEqua tion>(); | |
82 case SkXfermode::kHue_Mode: return RefFactory<kHSLHue_GrBlendEquation>() ; | |
83 case SkXfermode::kSaturation_Mode: return RefFactory<kHSLSaturation_GrBl endEquation>(); | |
84 case SkXfermode::kColor_Mode: return RefFactory<kHSLColor_GrBlendEquatio n>(); | |
85 case SkXfermode::kLuminosity_Mode: return RefFactory<kHSLLuminosity_GrBl endEquation>(); | |
86 } | |
87 | |
88 return NULL; | |
89 } | |
90 | |
91 template<GrBlendEquation Equation> GrXPFactory* GrAdvancedEquationXPFactory::Ref Factory() { | |
92 GR_STATIC_ASSERT(Equation > kLastBasicGrBlendEquation); | |
93 static GrAdvancedEquationXPFactory* factory; | |
94 if (!factory) { | |
95 factory = SkNEW_ARGS(GrAdvancedEquationXPFactory, (Equation)); | |
96 } | |
97 return SkRef(factory); | |
98 } | |
99 | |
100 bool GrAdvancedEquationXPFactory::canTweakAlphaForCoverage() const { | |
101 /* | |
102 The general SVG blend equation is defined in the spec as follows: | |
103 | |
104 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa) | |
105 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa) | |
106 | |
107 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha, | |
108 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied | |
109 RGB colors.) | |
110 | |
111 For every "advanced" blend mode, X=Y=Z=1 and this equation reduces to the | |
112 PDF blend equation. | |
113 | |
114 It can be shown that when X=Y=Z=1, canTweakAlphaForCoverage() is true. | |
115 | |
116 | |
117 == Color == | |
118 | |
119 We substitute Y=Z=1 and define a blend() function that calculates Dca' in | |
120 terms of premultiplied alpha only: | |
121 | |
122 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0, | |
123 Sca : if Da == 0, | |
124 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dc a * (1-Sa) : if Sa,Da != 0} | |
125 | |
126 And for coverage modulation, we use a post blend src-over model: | |
127 | |
128 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
129 | |
130 (Where f is the fractional coverage.) | |
131 | |
132 Next we show that canTweakAlphaForCoverage() is true by proving the | |
133 following relationship: | |
134 | |
135 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
136 | |
137 General case (f,Sa,Da != 0): | |
138 | |
139 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
140 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f ) * Dca [Sa,Da != 0, definition of blend()] | |
141 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca | |
142 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * S a + Dca - f*Dca | |
143 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca | |
144 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca | |
145 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) | |
146 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0] | |
147 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()] | |
148 | |
149 Corner cases (Sa=0, Da=0, and f=0): | |
150 | |
151 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
152 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()] | |
153 = Dca | |
154 = blend(0, Dca, 0, Da) [definition of blend()] | |
155 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0] | |
156 | |
157 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
158 = f * Sca + (1-f) * Dca [Da=0, definition of blend()] | |
159 = f * Sca [Da=0] | |
160 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()] | |
161 = blend(f*Sca, Dca, f*Sa, Da) [Da=0] | |
162 | |
163 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
164 = Dca [f=0] | |
165 = blend(0, Dca, 0, Da) [definition of blend()] | |
166 = blend(f*Sca, Dca, f*Sa, Da) [f=0] | |
167 | |
168 == Alpha == | |
169 | |
170 We substitute X=Y=Z=1 and define a blend() function that calculates Da': | |
171 | |
172 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa) | |
173 = Sa * Da + Sa - Sa * Da + Da - Da * Sa | |
174 = Sa + Da - Sa * Da | |
175 | |
176 We use the same model for coverage modulation as we did with color: | |
177 | |
178 Da'' = f * blend(Sa, Da) + (1-f) * Da | |
179 | |
180 And show that canTweakAlphaForCoverage() is true by proving the following | |
181 relationship: | |
182 | |
183 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da | |
184 | |
185 | |
186 f * blend(Sa, Da) + (1-f) * Da | |
187 = f * (Sa + Da - Sa * Da) + (1-f) * Da | |
188 = f*Sa + f*Da - f*Sa * Da + Da - f*Da | |
189 = f*Sa - f*Sa * Da + Da | |
190 = f*Sa + Da - f*Sa * Da | |
191 = blend(f*Sa, Da) | |
192 */ | |
193 | |
194 return true; | |
195 } | |
196 | |
197 bool GrAdvancedEquationXPFactory::supportsRGBCoverage(GrColor, uint32_t) const { | |
198 /* | |
199 We do coverage modulation by multiplying it into the src color before | |
200 blending. The GPU runs the blend equation *after* the fragment shader, so | |
201 it is not within our control to apply coverage at that point. This model | |
202 is mathematically correct, but it only works with scalar coverage. It's easy | |
203 to see why it won't work when coverage is unique per component: | |
204 | |
205 blend(fc*Sca, Dca, fa*Sa, Da) | |
206 = B((fc*Sca)/(fa*Sa), Dca/Da) * ... | |
207 != B(Sc, Dc) * ... | |
208 | |
209 The arguments for B() no longer reduce to (Sc, Dc). And when Sc == 0, the | |
210 arguments for B() work, but the equation still doesn't: | |
211 | |
212 fc * blend(0, Dca, Sa, Da) + (1-fc) * Dca | |
213 = ... | |
214 = B(0, Dca/Da) * fc*Sa * Da + fc*0 * (1-Da) + Dca * (1 - fc*Sa) | |
215 = blend(fc*0, Dca, fc*Sa, Da) | |
216 != blend(fc*0, Dca, fa*Sa, Da) | |
217 | |
218 (Note that this could work if we knew the dst was transparent black.) | |
219 */ | |
220 | |
221 return false; | |
222 } | |
223 | |
224 GrXferProcessor* GrAdvancedEquationXPFactory::onCreateXferProcessor( | |
225 const GrDrawTargetCaps& caps, | |
226 const GrProcOptInfo& col orPOI, | |
227 const GrProcOptInfo& cov eragePOI, | |
228 const GrDeviceCoordTextu re* dstCopy) const { | |
229 SkASSERT(!dstCopy || !dstCopy->texture()); | |
230 | |
231 if (!caps.advancedBlendEquationSupport()) { | |
232 return NULL; | |
233 } | |
234 if (coveragePOI.isFourChannelOutput()) { | |
235 // RGB coverage is not supported. (See supportsRGBCoverage()) | |
236 return NULL; | |
237 } | |
238 return AdvancedEquationXPBase::Create(!coveragePOI.isSolidWhite(), fEquation ); | |
239 } | |
240 | |
241 AdvancedEquationXPBase* AdvancedEquationXPBase::Create(bool hasCoverage, GrBlend Equation equation) { | |
egdaniel
2015/04/27 15:28:25
Okay so it seems like I may have confused things w
Chris Dalton
2015/04/27 18:25:39
This is also how I would prefer to do it, and I li
| |
242 if (hasCoverage) { | |
243 return SkNEW_ARGS(AdvancedEquationXP<true>, (equation)); | |
244 } else { | |
245 return SkNEW_ARGS(AdvancedEquationXP<false>, (equation)); | |
246 } | |
247 } | |
248 | |
249 bool AdvancedEquationXPBase::willNeedXferBarrier(const GrRenderTarget*, | |
250 const GrDrawTargetCaps& caps, | |
251 GrXferBarrierType* outBarrierTy pe) const { | |
252 if (GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport != caps.blendEq uationSupport()) { | |
253 *outBarrierType = kBlend_GrXferBarrierType; | |
254 return true; | |
255 } | |
256 return false; | |
257 } | |
258 | |
259 void AdvancedEquationXPBase::onGetGLProcessorKey(const GrGLCaps&, GrProcessorKey Builder*) const { | |
260 // Our XP's don't emit variable fragment code. (HasCoverage is accounted for by the class ID.) | |
261 return; | |
262 } | |
263 | |
264 template</*HasCoverage = false*/> | |
265 GrXferProcessor::OptFlags AdvancedEquationXP<false>::getOptimizations(const GrPr ocOptInfo&, | |
266 const GrPr ocOptInfo&, | |
267 bool, GrCo lor*, | |
268 const GrDr awTargetCaps&) { | |
269 return kIgnoreCoverage_OptFlag; | |
270 } | |
271 | |
272 template</*HasCoverage = false*/> | |
273 GrGLXferProcessor* AdvancedEquationXP<false>::createGLInstance() const { | |
274 class GLAdvancedEquationXP : public GrGLXferProcessor { | |
275 private: | |
276 void onEmitCode(const EmitArgs& args) override { | |
277 GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilde r(); | |
278 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputC olor); | |
279 } | |
280 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) ov erride {} | |
281 }; | |
282 return SkNEW(GLAdvancedEquationXP); | |
283 } | |
284 | |
285 template</*HasCoverage = true*/> | |
286 GrXferProcessor::OptFlags AdvancedEquationXP<true>::getOptimizations(const GrPro cOptInfo&, | |
287 const GrPro cOptInfo&, | |
288 bool, GrCol or*, | |
289 const GrDra wTargetCaps&) { | |
290 return kNone_Opt; | |
291 } | |
292 | |
293 template</*HasCoverage = true*/> | |
294 GrGLXferProcessor* AdvancedEquationXP<true>::createGLInstance() const { | |
295 class GLAdvancedEquationXPWithCoverage : public GrGLXferProcessor { | |
296 private: | |
297 void onEmitCode(const EmitArgs& args) override { | |
298 // We do coverage modulation by multiplying it into the src color be fore blending. | |
299 // (See canTweakAlphaForCoverage()) | |
300 GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilde r(); | |
301 fsBuilder->codeAppendf("%s = %s * %s;", | |
302 args.fOutputPrimary, args.fInputColor, args.f InputCoverage); | |
303 } | |
304 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) ov erride {} | |
305 }; | |
306 return SkNEW(GLAdvancedEquationXPWithCoverage); | |
307 } | |
308 | |
309 void AdvancedEquationXPBase::onGetBlendInfo(BlendInfo* blendInfo) const { | |
310 SkASSERT(GrBlendEquationIsAdvanced(fEquation)); | |
311 blendInfo->fEquation = fEquation; | |
312 } | |
313 | |
314 | |
315 GR_DEFINE_XP_FACTORY_TEST(GrAdvancedEquationXPFactory); | |
316 | |
317 GrXPFactory* GrAdvancedEquationXPFactory::TestCreate(SkRandom* random, | |
318 GrContext* context, | |
319 const GrDrawTargetCaps& cap s, | |
320 GrTexture*[]) { | |
321 int mode = random->nextRangeU(SkXfermode::kLastCoeffMode, SkXfermode::kLastM ode); | |
322 if (SkXfermode::kLastCoeffMode == mode) { | |
323 mode = SkXfermode::kScreen_Mode; | |
324 } | |
325 return GrAdvancedEquationXPFactory::Create(static_cast<SkXfermode::Mode>(mod e)); | |
326 } | |
OLD | NEW |