Flutter Engine
The Flutter Engine
GrCustomXfermode.cpp
Go to the documentation of this file.
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
9
13#include "src/base/SkRandom.h"
14#include "src/gpu/Blend.h"
15#include "src/gpu/KeyBuilder.h"
24
25#include <memory>
26#include <string>
27
29enum class GrClampType;
30
32 return (int)mode > (int)SkBlendMode::kLastCoeffMode &&
34}
35
36///////////////////////////////////////////////////////////////////////////////
37// Static helpers
38///////////////////////////////////////////////////////////////////////////////
39
41 constexpr int kEqOffset = ((int)skgpu::BlendEquation::kOverlay - (int)SkBlendMode::kOverlay);
42 static_assert((int)skgpu::BlendEquation::kOverlay == (int)SkBlendMode::kOverlay + kEqOffset);
43 static_assert((int)skgpu::BlendEquation::kDarken == (int)SkBlendMode::kDarken + kEqOffset);
44 static_assert((int)skgpu::BlendEquation::kLighten == (int)SkBlendMode::kLighten + kEqOffset);
45 static_assert((int)skgpu::BlendEquation::kColorDodge == (int)SkBlendMode::kColorDodge + kEqOffset);
46 static_assert((int)skgpu::BlendEquation::kColorBurn == (int)SkBlendMode::kColorBurn + kEqOffset);
47 static_assert((int)skgpu::BlendEquation::kHardLight == (int)SkBlendMode::kHardLight + kEqOffset);
48 static_assert((int)skgpu::BlendEquation::kSoftLight == (int)SkBlendMode::kSoftLight + kEqOffset);
49 static_assert((int)skgpu::BlendEquation::kDifference == (int)SkBlendMode::kDifference + kEqOffset);
50 static_assert((int)skgpu::BlendEquation::kExclusion == (int)SkBlendMode::kExclusion + kEqOffset);
51 static_assert((int)skgpu::BlendEquation::kMultiply == (int)SkBlendMode::kMultiply + kEqOffset);
52 static_assert((int)skgpu::BlendEquation::kHSLHue == (int)SkBlendMode::kHue + kEqOffset);
53 static_assert((int)skgpu::BlendEquation::kHSLSaturation == (int)SkBlendMode::kSaturation + kEqOffset);
54 static_assert((int)skgpu::BlendEquation::kHSLColor == (int)SkBlendMode::kColor + kEqOffset);
55 static_assert((int)skgpu::BlendEquation::kHSLLuminosity == (int)SkBlendMode::kLuminosity + kEqOffset);
56
57 // There's an illegal BlendEquation that corresponds to no SkBlendMode, hence the extra +1.
58 static_assert(skgpu::kBlendEquationCnt == (int)SkBlendMode::kLastMode + 1 + 1 + kEqOffset);
59
60 return static_cast<skgpu::BlendEquation>((int)mode + kEqOffset);
61#undef EQ_OFFSET
62}
63
66 if (!caps.advancedBlendEquationSupport()) {
67 return false;
68 }
70 return false; // LCD coverage must be applied after the blend equation.
71 }
72 if (caps.isAdvancedBlendEquationDisabled(equation)) {
73 return false;
74 }
75 return true;
76}
77
78///////////////////////////////////////////////////////////////////////////////
79// Xfer Processor
80///////////////////////////////////////////////////////////////////////////////
81
82class CustomXP : public GrXferProcessor {
83public:
86 , fMode(mode)
87 , fHWBlendEquation(hwBlendEquation) {}
88
90 : INHERITED(kCustomXP_ClassID, /*willReadDstColor=*/true, coverage)
91 , fMode(mode)
92 , fHWBlendEquation(skgpu::BlendEquation::kIllegal) {
93 }
94
95 const char* name() const override { return "Custom Xfermode"; }
96
97 std::unique_ptr<ProgramImpl> makeProgramImpl() const override;
98
99 GrXferBarrierType xferBarrierType(const GrCaps&) const override;
100
101private:
102 bool hasHWBlendEquation() const { return skgpu::BlendEquation::kIllegal != fHWBlendEquation; }
103
104 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override;
105
106 void onGetBlendInfo(skgpu::BlendInfo*) const override;
107
108 bool onIsEqual(const GrXferProcessor& xpBase) const override;
109
110 const SkBlendMode fMode;
111 const skgpu::BlendEquation fHWBlendEquation;
112
113 using INHERITED = GrXferProcessor;
114};
115
116void CustomXP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
117 if (this->hasHWBlendEquation()) {
118 SkASSERT(caps.fAdvBlendEqInteraction > 0); // 0 will mean !xp.hasHWBlendEquation().
119 b->addBool(true, "has hardware blend equation");
120 b->add32(caps.fAdvBlendEqInteraction);
121 } else {
122 b->addBool(false, "has hardware blend equation");
123 b->add32(GrGLSLBlend::BlendKey(fMode));
124 }
125}
126
127std::unique_ptr<GrXferProcessor::ProgramImpl> CustomXP::makeProgramImpl() const {
128 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
129
130 class Impl : public ProgramImpl {
131 private:
132 void emitOutputsForBlendState(const EmitArgs& args) override {
133 const CustomXP& xp = args.fXP.cast<CustomXP>();
134 SkASSERT(xp.hasHWBlendEquation());
135
136 GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
137 fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.fHWBlendEquation);
138
139 // Apply coverage by multiplying it into the src color before blending. This will "just
140 // work" automatically. (See analysisProperties())
141 fragBuilder->codeAppendf("%s = %s * %s;",
142 args.fOutputPrimary,
143 args.fInputCoverage,
144 args.fInputColor);
145 }
146
147 void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
148 GrGLSLUniformHandler* uniformHandler,
149 const char* srcColor,
150 const char* srcCoverage,
151 const char* dstColor,
152 const char* outColor,
153 const char* outColorSecondary,
154 const GrXferProcessor& proc) override {
155 const CustomXP& xp = proc.cast<CustomXP>();
156 SkASSERT(!xp.hasHWBlendEquation());
157
158 std::string blendExpr = GrGLSLBlend::BlendExpression(
159 &xp, uniformHandler, &fBlendUniform, srcColor, dstColor, xp.fMode);
160 fragBuilder->codeAppendf("%s = %s;", outColor, blendExpr.c_str());
161
162 // Apply coverage.
163 DefaultCoverageModulation(fragBuilder,
164 srcCoverage,
165 dstColor,
166 outColor,
167 outColorSecondary,
168 xp);
169 }
170
171 void onSetData(const GrGLSLProgramDataManager& pdman,
172 const GrXferProcessor& proc) override {
173 if (fBlendUniform.isValid()) {
174 const CustomXP& xp = proc.cast<CustomXP>();
175 GrGLSLBlend::SetBlendModeUniformData(pdman, fBlendUniform, xp.fMode);
176 }
177 }
178
180 };
181
182 return std::make_unique<Impl>();
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
191 if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
193 }
195}
196
197void CustomXP::onGetBlendInfo(skgpu::BlendInfo* blendInfo) const {
198 if (this->hasHWBlendEquation()) {
199 blendInfo->fEquation = fHWBlendEquation;
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
215public:
217 : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
218
219private:
222 const GrCaps&,
223 GrClampType) const override;
224
225 AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
227 const GrCaps&,
228 GrClampType) const override;
229
231
232 SkBlendMode fMode;
233 skgpu::BlendEquation fHWBlendEquation;
234
235 using INHERITED = GrXPFactory;
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(
247 const GrCaps& caps,
248 GrClampType clampType) const {
250 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
251 return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
252 }
253 return sk_sp<GrXferProcessor>(new CustomXP(fMode, coverage));
254}
255
256GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(
258 const GrCaps& caps, GrClampType clampType) const {
259 /*
260 The general SVG blend equation is defined in the spec as follows:
261
262 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
263 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
264
265 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
266 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
267 RGB colors.)
268
269 For every blend mode supported by this class, i.e. the "advanced" blend
270 modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
271
272 It can be shown that when X=Y=Z=1, these equations can modulate alpha for
273 coverage.
274
275
276 == Color ==
277
278 We substitute Y=Z=1 and define a blend() function that calculates Dca' in
279 terms of premultiplied alpha only:
280
281 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
282 Sca : if Da == 0,
283 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if
284 Sa,Da != 0}
285
286 And for coverage modulation, we use a post blend src-over model:
287
288 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
289
290 (Where f is the fractional coverage.)
291
292 Next we show that canTweakAlphaForCoverage() is true by proving the
293 following relationship:
294
295 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
296
297 General case (f,Sa,Da != 0):
298
299 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
300 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da !=
301 0, definition of blend()]
302 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
303 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
304 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
305 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
306 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
307 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
308 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
309
310 Corner cases (Sa=0, Da=0, and f=0):
311
312 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
313 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
314 = Dca
315 = blend(0, Dca, 0, Da) [definition of blend()]
316 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
317
318 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
319 = f * Sca + (1-f) * Dca [Da=0, definition of blend()]
320 = f * Sca [Da=0]
321 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
322 = blend(f*Sca, Dca, f*Sa, Da) [Da=0]
323
324 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
325 = Dca [f=0]
326 = blend(0, Dca, 0, Da) [definition of blend()]
327 = blend(f*Sca, Dca, f*Sa, Da) [f=0]
328
329 == Alpha ==
330
331 We substitute X=Y=Z=1 and define a blend() function that calculates Da':
332
333 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
334 = Sa * Da + Sa - Sa * Da + Da - Da * Sa
335 = Sa + Da - Sa * Da
336
337 We use the same model for coverage modulation as we did with color:
338
339 Da'' = f * blend(Sa, Da) + (1-f) * Da
340
341 And show that canTweakAlphaForCoverage() is true by proving the following
342 relationship:
343
344 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
345
346
347 f * blend(Sa, Da) + (1-f) * Da
348 = f * (Sa + Da - Sa * Da) + (1-f) * Da
349 = f*Sa + f*Da - f*Sa * Da + Da - f*Da
350 = f*Sa - f*Sa * Da + Da
351 = f*Sa + Da - f*Sa * Da
352 = blend(f*Sa, Da)
353 */
354 if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
357 } else {
361 }
362 }
365}
366
368#if defined(GR_TEST_UTILS)
369const GrXPFactory* CustomXPFactory::TestGet(GrProcessorTestData* d) {
370 int mode = d->fRandom->nextRangeU((int)SkBlendMode::kLastCoeffMode + 1,
372
374}
375#endif
376
377///////////////////////////////////////////////////////////////////////////////
378
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) {
396 return &gOverlay;
398 return &gDarken;
400 return &gLighten;
402 return &gColorDodge;
404 return &gColorBurn;
406 return &gHardLight;
408 return &gSoftLight;
410 return &gDifference;
412 return &gExclusion;
414 return &gMultiply;
416 return &gHue;
418 return &gSaturation;
420 return &gColor;
422 return &gLuminosity;
423 default:
425 return nullptr;
426 }
427}
static constexpr skgpu::BlendEquation hw_blend_equation(SkBlendMode mode)
static bool can_use_hw_blend_equation(skgpu::BlendEquation equation, GrProcessorAnalysisCoverage coverage, const GrCaps &caps)
GrProcessorAnalysisCoverage
#define GR_DECLARE_XP_FACTORY_TEST
#define GR_DEFINE_XP_FACTORY_TEST(...)
GrClampType
Definition: GrTypesPriv.h:228
GrXferBarrierType
@ kNone_GrXferBarrierType
@ kBlend_GrXferBarrierType
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kLastCoeffMode
last porter duff blend mode
@ kColorDodge
brighten destination to reflect source
@ kLastSeparableMode
last blend mode operating separately on components
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kLastMode
last valid value
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
constexpr CustomXPFactory(SkBlendMode mode)
std::unique_ptr< ProgramImpl > makeProgramImpl() const override
const char * name() const override
CustomXP(SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
GrXferBarrierType xferBarrierType(const GrCaps &) const override
CustomXP(SkBlendMode mode, skgpu::BlendEquation hwBlendEquation)
Definition: GrCaps.h:57
bool advancedBlendEquationSupport() const
Definition: GrCaps.h:162
@ kAdvancedCoherent_BlendEquationSupport
Definition: GrCaps.h:154
bool advancedCoherentBlendEquationSupport() const
Definition: GrCaps.h:166
bool isAdvancedBlendEquationDisabled(skgpu::BlendEquation equation) const
Definition: GrCaps.h:170
BlendEquationSupport blendEquationSupport() const
Definition: GrCaps.h:160
void codeAppendf(const char format[],...) SK_PRINTF_LIKE(2
GrGLSLProgramDataManager::UniformHandle UniformHandle
virtual void enableAdvancedBlendEquationIfNeeded(skgpu::BlendEquation)=0
const T & cast() const
Definition: GrProcessor.h:127
@ kCustomXP_ClassID
Definition: GrProcessor.h:37
constexpr GrXPFactory()
GrXferProcessor(ClassID classID)
bool willReadDstColor() const
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct s
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
bool IsSupportedMode(SkBlendMode mode)
const GrXPFactory * Get(SkBlendMode mode)
std::string BlendExpression(const GrProcessor *processor, GrGLSLUniformHandler *uniformHandler, GrGLSLProgramDataManager::UniformHandle *blendUniform, const char *srcColor, const char *dstColor, SkBlendMode mode)
Definition: GrGLSLBlend.cpp:20
int BlendKey(SkBlendMode mode)
Definition: GrGLSLBlend.cpp:40
void SetBlendModeUniformData(const GrGLSLProgramDataManager &pdman, GrGLSLProgramDataManager::UniformHandle blendUniform, SkBlendMode mode)
Definition: GrGLSLBlend.cpp:73
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
Definition: GpuTools.h:21
static const int kBlendEquationCnt
Definition: Blend.h:55
BlendEquation
Definition: Blend.h:26
AdvBlendEqInteraction fAdvBlendEqInteraction
Definition: SkSLUtil.h:162