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