2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/gpu/ganesh/effects/GrCustomXfermode.h"
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/GrShaderCaps.h"
16 #include "src/gpu/ganesh/GrXferProcessor.h"
17 #include "src/gpu/ganesh/glsl/GrGLSLBlend.h"
18 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
19 #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
20 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
22 bool GrCustomXfermode::IsSupportedMode(SkBlendMode mode) {
23 return (int)mode > (int)SkBlendMode::kLastCoeffMode &&
24 (int)mode <= (int)SkBlendMode::kLastMode;
27 ///////////////////////////////////////////////////////////////////////////////
29 ///////////////////////////////////////////////////////////////////////////////
31 static constexpr skgpu::BlendEquation hw_blend_equation(SkBlendMode mode) {
32 constexpr int kEqOffset = ((int)skgpu::BlendEquation::kOverlay - (int)SkBlendMode::kOverlay);
33 static_assert((int)skgpu::BlendEquation::kOverlay == (int)SkBlendMode::kOverlay + kEqOffset);
34 static_assert((int)skgpu::BlendEquation::kDarken == (int)SkBlendMode::kDarken + kEqOffset);
35 static_assert((int)skgpu::BlendEquation::kLighten == (int)SkBlendMode::kLighten + kEqOffset);
36 static_assert((int)skgpu::BlendEquation::kColorDodge == (int)SkBlendMode::kColorDodge + kEqOffset);
37 static_assert((int)skgpu::BlendEquation::kColorBurn == (int)SkBlendMode::kColorBurn + kEqOffset);
38 static_assert((int)skgpu::BlendEquation::kHardLight == (int)SkBlendMode::kHardLight + kEqOffset);
39 static_assert((int)skgpu::BlendEquation::kSoftLight == (int)SkBlendMode::kSoftLight + kEqOffset);
40 static_assert((int)skgpu::BlendEquation::kDifference == (int)SkBlendMode::kDifference + kEqOffset);
41 static_assert((int)skgpu::BlendEquation::kExclusion == (int)SkBlendMode::kExclusion + kEqOffset);
42 static_assert((int)skgpu::BlendEquation::kMultiply == (int)SkBlendMode::kMultiply + kEqOffset);
43 static_assert((int)skgpu::BlendEquation::kHSLHue == (int)SkBlendMode::kHue + kEqOffset);
44 static_assert((int)skgpu::BlendEquation::kHSLSaturation == (int)SkBlendMode::kSaturation + kEqOffset);
45 static_assert((int)skgpu::BlendEquation::kHSLColor == (int)SkBlendMode::kColor + kEqOffset);
46 static_assert((int)skgpu::BlendEquation::kHSLLuminosity == (int)SkBlendMode::kLuminosity + kEqOffset);
48 // There's an illegal BlendEquation that corresponds to no SkBlendMode, hence the extra +1.
49 static_assert(skgpu::kBlendEquationCnt == (int)SkBlendMode::kLastMode + 1 + 1 + kEqOffset);
51 return static_cast<skgpu::BlendEquation>((int)mode + kEqOffset);
55 static bool can_use_hw_blend_equation(skgpu::BlendEquation equation,
56 GrProcessorAnalysisCoverage coverage, const GrCaps& caps) {
57 if (!caps.advancedBlendEquationSupport()) {
60 if (GrProcessorAnalysisCoverage::kLCD == coverage) {
61 return false; // LCD coverage must be applied after the blend equation.
63 if (caps.isAdvancedBlendEquationDisabled(equation)) {
69 ///////////////////////////////////////////////////////////////////////////////
71 ///////////////////////////////////////////////////////////////////////////////
73 class CustomXP : public GrXferProcessor {
75 CustomXP(SkBlendMode mode, skgpu::BlendEquation hwBlendEquation)
76 : INHERITED(kCustomXP_ClassID)
78 , fHWBlendEquation(hwBlendEquation) {}
80 CustomXP(SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
81 : INHERITED(kCustomXP_ClassID, /*willReadDstColor=*/true, coverage)
83 , fHWBlendEquation(skgpu::BlendEquation::kIllegal) {
86 const char* name() const override { return "Custom Xfermode"; }
88 std::unique_ptr<ProgramImpl> makeProgramImpl() const override;
90 GrXferBarrierType xferBarrierType(const GrCaps&) const override;
93 bool hasHWBlendEquation() const { return skgpu::BlendEquation::kIllegal != fHWBlendEquation; }
95 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override;
97 void onGetBlendInfo(BlendInfo*) const override;
99 bool onIsEqual(const GrXferProcessor& xpBase) const override;
101 const SkBlendMode fMode;
102 const skgpu::BlendEquation fHWBlendEquation;
104 using INHERITED = GrXferProcessor;
107 void CustomXP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
108 if (this->hasHWBlendEquation()) {
109 SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
110 b->addBool(true, "has hardware blend equation");
111 b->add32(caps.advBlendEqInteraction());
113 b->addBool(false, "has hardware blend equation");
114 b->add32(GrGLSLBlend::BlendKey(fMode));
118 std::unique_ptr<GrXferProcessor::ProgramImpl> CustomXP::makeProgramImpl() const {
119 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
121 class Impl : public ProgramImpl {
123 void emitOutputsForBlendState(const EmitArgs& args) override {
124 const CustomXP& xp = args.fXP.cast<CustomXP>();
125 SkASSERT(xp.hasHWBlendEquation());
127 GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
128 fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.fHWBlendEquation);
130 // Apply coverage by multiplying it into the src color before blending. This will "just
131 // work" automatically. (See analysisProperties())
132 fragBuilder->codeAppendf("%s = %s * %s;",
138 void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
139 GrGLSLUniformHandler* uniformHandler,
140 const char* srcColor,
141 const char* srcCoverage,
142 const char* dstColor,
143 const char* outColor,
144 const char* outColorSecondary,
145 const GrXferProcessor& proc) override {
146 const CustomXP& xp = proc.cast<CustomXP>();
147 SkASSERT(!xp.hasHWBlendEquation());
149 std::string blendExpr = GrGLSLBlend::BlendExpression(
150 &xp, uniformHandler, &fBlendUniform, srcColor, dstColor, xp.fMode);
151 fragBuilder->codeAppendf("%s = %s;", outColor, blendExpr.c_str());
154 DefaultCoverageModulation(fragBuilder,
162 void onSetData(const GrGLSLProgramDataManager& pdman,
163 const GrXferProcessor& proc) override {
164 if (fBlendUniform.isValid()) {
165 const CustomXP& xp = proc.cast<CustomXP>();
166 GrGLSLBlend::SetBlendModeUniformData(pdman, fBlendUniform, xp.fMode);
170 GrGLSLUniformHandler::UniformHandle fBlendUniform;
173 return std::make_unique<Impl>();
176 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
177 const CustomXP& s = other.cast<CustomXP>();
178 return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
181 GrXferBarrierType CustomXP::xferBarrierType(const GrCaps& caps) const {
182 if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
183 return kBlend_GrXferBarrierType;
185 return kNone_GrXferBarrierType;
188 void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
189 if (this->hasHWBlendEquation()) {
190 blendInfo->fEquation = fHWBlendEquation;
194 ///////////////////////////////////////////////////////////////////////////////
196 // See the comment above GrXPFactory's definition about this warning suppression.
197 #if defined(__GNUC__)
198 #pragma GCC diagnostic push
199 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
201 #if defined(__clang__)
202 #pragma clang diagnostic push
203 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
205 class CustomXPFactory : public GrXPFactory {
207 constexpr CustomXPFactory(SkBlendMode mode)
208 : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
211 sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
212 GrProcessorAnalysisCoverage,
214 GrClampType) const override;
216 AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
217 const GrProcessorAnalysisCoverage&,
219 GrClampType) const override;
221 GR_DECLARE_XP_FACTORY_TEST
224 skgpu::BlendEquation fHWBlendEquation;
226 using INHERITED = GrXPFactory;
228 #if defined(__GNUC__)
229 #pragma GCC diagnostic pop
231 #if defined(__clang__)
232 #pragma clang diagnostic pop
235 sk_sp<const GrXferProcessor> CustomXPFactory::makeXferProcessor(
236 const GrProcessorAnalysisColor&,
237 GrProcessorAnalysisCoverage coverage,
239 GrClampType clampType) const {
240 SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
241 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
242 return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
244 return sk_sp<GrXferProcessor>(new CustomXP(fMode, coverage));
247 GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(
248 const GrProcessorAnalysisColor&, const GrProcessorAnalysisCoverage& coverage,
249 const GrCaps& caps, GrClampType clampType) const {
251 The general SVG blend equation is defined in the spec as follows:
253 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
254 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
256 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
257 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
260 For every blend mode supported by this class, i.e. the "advanced" blend
261 modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
263 It can be shown that when X=Y=Z=1, these equations can modulate alpha for
269 We substitute Y=Z=1 and define a blend() function that calculates Dca' in
270 terms of premultiplied alpha only:
272 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
274 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if
277 And for coverage modulation, we use a post blend src-over model:
279 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
281 (Where f is the fractional coverage.)
283 Next we show that canTweakAlphaForCoverage() is true by proving the
284 following relationship:
286 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
288 General case (f,Sa,Da != 0):
290 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
291 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da !=
292 0, definition of blend()]
293 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
294 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
295 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
296 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
297 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
298 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
299 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
301 Corner cases (Sa=0, Da=0, and f=0):
303 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
304 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
306 = blend(0, Dca, 0, Da) [definition of blend()]
307 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
309 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
310 = f * Sca + (1-f) * Dca [Da=0, definition of blend()]
312 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
313 = blend(f*Sca, Dca, f*Sa, Da) [Da=0]
315 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
317 = blend(0, Dca, 0, Da) [definition of blend()]
318 = blend(f*Sca, Dca, f*Sa, Da) [f=0]
322 We substitute X=Y=Z=1 and define a blend() function that calculates Da':
324 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
325 = Sa * Da + Sa - Sa * Da + Da - Da * Sa
328 We use the same model for coverage modulation as we did with color:
330 Da'' = f * blend(Sa, Da) + (1-f) * Da
332 And show that canTweakAlphaForCoverage() is true by proving the following
335 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
338 f * blend(Sa, Da) + (1-f) * Da
339 = f * (Sa + Da - Sa * Da) + (1-f) * Da
340 = f*Sa + f*Da - f*Sa * Da + Da - f*Da
341 = f*Sa - f*Sa * Da + Da
342 = f*Sa + Da - f*Sa * Da
345 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
346 if (caps.blendEquationSupport() == GrCaps::kAdvancedCoherent_BlendEquationSupport) {
347 return AnalysisProperties::kCompatibleWithCoverageAsAlpha;
349 return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
350 AnalysisProperties::kRequiresNonOverlappingDraws |
351 AnalysisProperties::kUsesNonCoherentHWBlending;
354 return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
355 AnalysisProperties::kReadsDstInShader;
358 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory);
360 const GrXPFactory* CustomXPFactory::TestGet(GrProcessorTestData* d) {
361 int mode = d->fRandom->nextRangeU((int)SkBlendMode::kLastCoeffMode + 1,
362 (int)SkBlendMode::kLastSeparableMode);
364 return GrCustomXfermode::Get((SkBlendMode)mode);
368 ///////////////////////////////////////////////////////////////////////////////
370 const GrXPFactory* GrCustomXfermode::Get(SkBlendMode mode) {
371 static constexpr const CustomXPFactory gOverlay(SkBlendMode::kOverlay);
372 static constexpr const CustomXPFactory gDarken(SkBlendMode::kDarken);
373 static constexpr const CustomXPFactory gLighten(SkBlendMode::kLighten);
374 static constexpr const CustomXPFactory gColorDodge(SkBlendMode::kColorDodge);
375 static constexpr const CustomXPFactory gColorBurn(SkBlendMode::kColorBurn);
376 static constexpr const CustomXPFactory gHardLight(SkBlendMode::kHardLight);
377 static constexpr const CustomXPFactory gSoftLight(SkBlendMode::kSoftLight);
378 static constexpr const CustomXPFactory gDifference(SkBlendMode::kDifference);
379 static constexpr const CustomXPFactory gExclusion(SkBlendMode::kExclusion);
380 static constexpr const CustomXPFactory gMultiply(SkBlendMode::kMultiply);
381 static constexpr const CustomXPFactory gHue(SkBlendMode::kHue);
382 static constexpr const CustomXPFactory gSaturation(SkBlendMode::kSaturation);
383 static constexpr const CustomXPFactory gColor(SkBlendMode::kColor);
384 static constexpr const CustomXPFactory gLuminosity(SkBlendMode::kLuminosity);
386 case SkBlendMode::kOverlay:
388 case SkBlendMode::kDarken:
390 case SkBlendMode::kLighten:
392 case SkBlendMode::kColorDodge:
394 case SkBlendMode::kColorBurn:
396 case SkBlendMode::kHardLight:
398 case SkBlendMode::kSoftLight:
400 case SkBlendMode::kDifference:
402 case SkBlendMode::kExclusion:
404 case SkBlendMode::kMultiply:
406 case SkBlendMode::kHue:
408 case SkBlendMode::kSaturation:
410 case SkBlendMode::kColor:
412 case SkBlendMode::kLuminosity:
415 SkASSERT(!GrCustomXfermode::IsSupportedMode(mode));