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/GrXferProcessor.h"
9
10 #include "src/gpu/GrCaps.h"
11 #include "src/gpu/GrPipeline.h"
12 #include "src/gpu/KeyBuilder.h"
13 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
14 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
15
GrXferProcessor(ClassID classID)16 GrXferProcessor::GrXferProcessor(ClassID classID)
17 : INHERITED(classID)
18 , fWillReadDstColor(false)
19 , fIsLCD(false) {}
20
GrXferProcessor(ClassID classID,bool willReadDstColor,GrProcessorAnalysisCoverage coverage)21 GrXferProcessor::GrXferProcessor(ClassID classID, bool willReadDstColor,
22 GrProcessorAnalysisCoverage coverage)
23 : INHERITED(classID)
24 , fWillReadDstColor(willReadDstColor)
25 , fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {}
26
hasSecondaryOutput() const27 bool GrXferProcessor::hasSecondaryOutput() const {
28 if (!this->willReadDstColor()) {
29 return this->onHasSecondaryOutput();
30 }
31 return false;
32 }
33
addToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b,const GrSurfaceOrigin * originIfDstTexture,bool usesInputAttachmentForDstRead) const34 void GrXferProcessor::addToKey(const GrShaderCaps& caps,
35 skgpu::KeyBuilder* b,
36 const GrSurfaceOrigin* originIfDstTexture,
37 bool usesInputAttachmentForDstRead) const {
38 uint32_t key = this->willReadDstColor() ? 0x1 : 0x0;
39 if (key) {
40 if (originIfDstTexture) {
41 key |= 0x2;
42 if (kTopLeft_GrSurfaceOrigin == *originIfDstTexture) {
43 key |= 0x4;
44 }
45 if (usesInputAttachmentForDstRead) {
46 key |= 0x8;
47 }
48 }
49 }
50 if (fIsLCD) {
51 key |= 0x10;
52 }
53 b->add32(key);
54 this->onAddToKey(caps, b);
55 }
56
57 #ifdef SK_DEBUG
equation_string(GrBlendEquation eq)58 static const char* equation_string(GrBlendEquation eq) {
59 switch (eq) {
60 case kAdd_GrBlendEquation:
61 return "add";
62 case kSubtract_GrBlendEquation:
63 return "subtract";
64 case kReverseSubtract_GrBlendEquation:
65 return "reverse_subtract";
66 case kScreen_GrBlendEquation:
67 return "screen";
68 case kOverlay_GrBlendEquation:
69 return "overlay";
70 case kDarken_GrBlendEquation:
71 return "darken";
72 case kLighten_GrBlendEquation:
73 return "lighten";
74 case kColorDodge_GrBlendEquation:
75 return "color_dodge";
76 case kColorBurn_GrBlendEquation:
77 return "color_burn";
78 case kHardLight_GrBlendEquation:
79 return "hard_light";
80 case kSoftLight_GrBlendEquation:
81 return "soft_light";
82 case kDifference_GrBlendEquation:
83 return "difference";
84 case kExclusion_GrBlendEquation:
85 return "exclusion";
86 case kMultiply_GrBlendEquation:
87 return "multiply";
88 case kHSLHue_GrBlendEquation:
89 return "hsl_hue";
90 case kHSLSaturation_GrBlendEquation:
91 return "hsl_saturation";
92 case kHSLColor_GrBlendEquation:
93 return "hsl_color";
94 case kHSLLuminosity_GrBlendEquation:
95 return "hsl_luminosity";
96 case kIllegal_GrBlendEquation:
97 SkASSERT(false);
98 return "<illegal>";
99 }
100 return "";
101 }
102
coeff_string(GrBlendCoeff coeff)103 static const char* coeff_string(GrBlendCoeff coeff) {
104 switch (coeff) {
105 case kZero_GrBlendCoeff:
106 return "zero";
107 case kOne_GrBlendCoeff:
108 return "one";
109 case kSC_GrBlendCoeff:
110 return "src_color";
111 case kISC_GrBlendCoeff:
112 return "inv_src_color";
113 case kDC_GrBlendCoeff:
114 return "dst_color";
115 case kIDC_GrBlendCoeff:
116 return "inv_dst_color";
117 case kSA_GrBlendCoeff:
118 return "src_alpha";
119 case kISA_GrBlendCoeff:
120 return "inv_src_alpha";
121 case kDA_GrBlendCoeff:
122 return "dst_alpha";
123 case kIDA_GrBlendCoeff:
124 return "inv_dst_alpha";
125 case kConstC_GrBlendCoeff:
126 return "const_color";
127 case kIConstC_GrBlendCoeff:
128 return "inv_const_color";
129 case kS2C_GrBlendCoeff:
130 return "src2_color";
131 case kIS2C_GrBlendCoeff:
132 return "inv_src2_color";
133 case kS2A_GrBlendCoeff:
134 return "src2_alpha";
135 case kIS2A_GrBlendCoeff:
136 return "inv_src2_alpha";
137 case kIllegal_GrBlendCoeff:
138 SkASSERT(false);
139 return "<illegal>";
140 }
141 return "";
142 }
143
dump() const144 SkString GrXferProcessor::BlendInfo::dump() const {
145 SkString out;
146 out.printf("write_color(%d) equation(%s) src_coeff(%s) dst_coeff:(%s) const(0x%08x)",
147 fWriteColor, equation_string(fEquation), coeff_string(fSrcBlend),
148 coeff_string(fDstBlend), fBlendConstant.toBytes_RGBA());
149 return out;
150 }
151 #endif
152
153 ///////////////////////////////////////////////////////////////////////////////
154
GetAnalysisProperties(const GrXPFactory * factory,const GrProcessorAnalysisColor & color,const GrProcessorAnalysisCoverage & coverage,const GrCaps & caps,GrClampType clampType)155 GrXPFactory::AnalysisProperties GrXPFactory::GetAnalysisProperties(
156 const GrXPFactory* factory,
157 const GrProcessorAnalysisColor& color,
158 const GrProcessorAnalysisCoverage& coverage,
159 const GrCaps& caps,
160 GrClampType clampType) {
161 AnalysisProperties result;
162 if (factory) {
163 result = factory->analysisProperties(color, coverage, caps, clampType);
164 } else {
165 result = GrPorterDuffXPFactory::SrcOverAnalysisProperties(color, coverage, caps, clampType);
166 }
167 if (coverage == GrProcessorAnalysisCoverage::kNone) {
168 result |= AnalysisProperties::kCompatibleWithCoverageAsAlpha;
169 }
170 SkASSERT(!(result & AnalysisProperties::kRequiresDstTexture));
171 if ((result & AnalysisProperties::kReadsDstInShader) &&
172 !caps.shaderCaps()->dstReadInShaderSupport()) {
173 result |= AnalysisProperties::kRequiresDstTexture |
174 AnalysisProperties::kRequiresNonOverlappingDraws;
175 }
176 return result;
177 }
178
MakeXferProcessor(const GrXPFactory * factory,const GrProcessorAnalysisColor & color,GrProcessorAnalysisCoverage coverage,const GrCaps & caps,GrClampType clampType)179 sk_sp<const GrXferProcessor> GrXPFactory::MakeXferProcessor(const GrXPFactory* factory,
180 const GrProcessorAnalysisColor& color,
181 GrProcessorAnalysisCoverage coverage,
182 const GrCaps& caps,
183 GrClampType clampType) {
184 if (factory) {
185 return factory->makeXferProcessor(color, coverage, caps, clampType);
186 } else {
187 return GrPorterDuffXPFactory::MakeSrcOverXferProcessor(color, coverage, caps);
188 }
189 }
190
191 //////////////////////////////////////////////////////////////////////////////
192
193 using ProgramImpl = GrXferProcessor::ProgramImpl;
194
195 // This is only called for cases where we are doing LCD coverage and not using in shader blending.
196 // For these cases we assume the the src alpha is 1, thus we can just use the max for the alpha
197 // coverage since src alpha will always be greater than or equal to dst alpha.
adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder * fragBuilder,const char * srcCoverage,const GrXferProcessor & proc)198 static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder,
199 const char* srcCoverage,
200 const GrXferProcessor& proc) {
201 if (srcCoverage && proc.isLCD()) {
202 fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);",
203 srcCoverage,
204 srcCoverage,
205 srcCoverage,
206 srcCoverage);
207 }
208 }
209
emitCode(const EmitArgs & args)210 void ProgramImpl::emitCode(const EmitArgs& args) {
211 if (!args.fXP.willReadDstColor()) {
212 adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP);
213 this->emitOutputsForBlendState(args);
214 } else {
215 GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
216 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
217 const char* dstColor = fragBuilder->dstColor();
218
219 bool needsLocalOutColor = false;
220
221 if (args.fDstTextureSamplerHandle.isValid()) {
222 if (args.fInputCoverage) {
223 // We don't think any shaders actually output negative coverage, but just as a
224 // safety check for floating point precision errors, we compare with <= here. We
225 // just check the RGB values of the coverage, since the alpha may not have been set
226 // when using LCD. If we are using single-channel coverage, alpha will be equal to
227 // RGB anyway.
228 //
229 // The discard here also helps for batching text-draws together, which need to read
230 // from a dst copy for blends. However, this only helps the case where the outer
231 // bounding boxes of each letter overlap and not two actually parts of the text.
232 fragBuilder->codeAppendf("if (all(lessThanEqual(%s.rgb, half3(0)))) {"
233 " discard;"
234 "}",
235 args.fInputCoverage);
236 }
237 } else {
238 needsLocalOutColor = args.fShaderCaps->requiresLocalOutputColorForFBFetch();
239 }
240
241 const char* outColor = "_localColorOut";
242 if (!needsLocalOutColor) {
243 outColor = args.fOutputPrimary;
244 } else {
245 fragBuilder->codeAppendf("half4 %s;", outColor);
246 }
247
248 this->emitBlendCodeForDstRead(fragBuilder,
249 uniformHandler,
250 args.fInputColor,
251 args.fInputCoverage,
252 dstColor,
253 outColor,
254 args.fOutputSecondary,
255 args.fXP);
256 if (needsLocalOutColor) {
257 fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, outColor);
258 }
259 }
260
261 // Swizzle the fragment shader outputs if necessary.
262 this->emitWriteSwizzle(args.fXPFragBuilder,
263 args.fWriteSwizzle,
264 args.fOutputPrimary,
265 args.fOutputSecondary);
266 }
267
emitWriteSwizzle(GrGLSLXPFragmentBuilder * x,const skgpu::Swizzle & swizzle,const char * outColor,const char * outColorSecondary) const268 void ProgramImpl::emitWriteSwizzle(GrGLSLXPFragmentBuilder* x,
269 const skgpu::Swizzle& swizzle,
270 const char* outColor,
271 const char* outColorSecondary) const {
272 if (skgpu::Swizzle::RGBA() != swizzle) {
273 x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.asString().c_str());
274 if (outColorSecondary) {
275 x->codeAppendf("%s = %s.%s;",
276 outColorSecondary,
277 outColorSecondary,
278 swizzle.asString().c_str());
279 }
280 }
281 }
282
setData(const GrGLSLProgramDataManager & pdm,const GrXferProcessor & xp)283 void ProgramImpl::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp) {
284 this->onSetData(pdm, xp);
285 }
286
DefaultCoverageModulation(GrGLSLXPFragmentBuilder * fragBuilder,const char * srcCoverage,const char * dstColor,const char * outColor,const char * outColorSecondary,const GrXferProcessor & proc)287 void ProgramImpl::DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder,
288 const char* srcCoverage,
289 const char* dstColor,
290 const char* outColor,
291 const char* outColorSecondary,
292 const GrXferProcessor& proc) {
293 if (srcCoverage) {
294 if (proc.isLCD()) {
295 fragBuilder->codeAppendf("half3 lerpRGB = mix(%s.aaa, %s.aaa, %s.rgb);",
296 dstColor,
297 outColor,
298 srcCoverage);
299 }
300 fragBuilder->codeAppendf("%s = %s * %s + (half4(1.0) - %s) * %s;",
301 outColor,
302 srcCoverage,
303 outColor,
304 srcCoverage,
305 dstColor);
306 if (proc.isLCD()) {
307 fragBuilder->codeAppendf("%s.a = max(max(lerpRGB.r, lerpRGB.b), lerpRGB.g);", outColor);
308 }
309 }
310 }
311