• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ganesh/GrXferProcessor.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkString.h"
12 #include "src/gpu/KeyBuilder.h"
13 #include "src/gpu/ganesh/GrCaps.h"
14 #include "src/gpu/ganesh/GrShaderCaps.h"
15 #include "src/gpu/ganesh/effects/GrCustomXfermode.h"
16 #include "src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h"
17 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
18 
19 #include <cstdint>
20 
21 enum class GrClampType;
22 
GrXferProcessor(ClassID classID)23 GrXferProcessor::GrXferProcessor(ClassID classID)
24         : INHERITED(classID)
25         , fWillReadDstColor(false)
26         , fIsLCD(false) {}
27 
GrXferProcessor(ClassID classID,bool willReadDstColor,GrProcessorAnalysisCoverage coverage)28 GrXferProcessor::GrXferProcessor(ClassID classID, bool willReadDstColor,
29                                  GrProcessorAnalysisCoverage coverage)
30         : INHERITED(classID)
31         , fWillReadDstColor(willReadDstColor)
32         , fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {}
33 
hasSecondaryOutput() const34 bool GrXferProcessor::hasSecondaryOutput() const {
35     if (!this->willReadDstColor()) {
36         return this->onHasSecondaryOutput();
37     }
38     return false;
39 }
40 
addToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b,const GrSurfaceOrigin * originIfDstTexture,bool usesInputAttachmentForDstRead) const41 void GrXferProcessor::addToKey(const GrShaderCaps& caps,
42                                skgpu::KeyBuilder* b,
43                                const GrSurfaceOrigin* originIfDstTexture,
44                                bool usesInputAttachmentForDstRead) const {
45     uint32_t key = this->willReadDstColor() ? 0x1 : 0x0;
46     if (key) {
47         if (originIfDstTexture) {
48             key |= 0x2;
49             if (kTopLeft_GrSurfaceOrigin == *originIfDstTexture) {
50                 key |= 0x4;
51             }
52             if (usesInputAttachmentForDstRead) {
53                 key |= 0x8;
54             }
55         }
56     }
57     if (fIsLCD) {
58         key |= 0x10;
59     }
60     b->add32(key);
61     this->onAddToKey(caps, b);
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////
65 
GetAnalysisProperties(const GrXPFactory * factory,const GrProcessorAnalysisColor & color,const GrProcessorAnalysisCoverage & coverage,const GrCaps & caps,GrClampType clampType)66 GrXPFactory::AnalysisProperties GrXPFactory::GetAnalysisProperties(
67         const GrXPFactory* factory,
68         const GrProcessorAnalysisColor& color,
69         const GrProcessorAnalysisCoverage& coverage,
70         const GrCaps& caps,
71         GrClampType clampType) {
72     AnalysisProperties result;
73     if (factory) {
74         result = factory->analysisProperties(color, coverage, caps, clampType);
75     } else {
76         result = GrPorterDuffXPFactory::SrcOverAnalysisProperties(color, coverage, caps, clampType);
77     }
78     if (coverage == GrProcessorAnalysisCoverage::kNone) {
79         result |= AnalysisProperties::kCompatibleWithCoverageAsAlpha;
80     }
81     SkASSERT(!(result & AnalysisProperties::kRequiresDstTexture));
82     if ((result & AnalysisProperties::kReadsDstInShader) &&
83         !caps.shaderCaps()->fDstReadInShaderSupport) {
84         result |= AnalysisProperties::kRequiresDstTexture |
85                   AnalysisProperties::kRequiresNonOverlappingDraws;
86     }
87     return result;
88 }
89 
MakeXferProcessor(const GrXPFactory * factory,const GrProcessorAnalysisColor & color,GrProcessorAnalysisCoverage coverage,const GrCaps & caps,GrClampType clampType)90 sk_sp<const GrXferProcessor> GrXPFactory::MakeXferProcessor(const GrXPFactory* factory,
91                                                             const GrProcessorAnalysisColor& color,
92                                                             GrProcessorAnalysisCoverage coverage,
93                                                             const GrCaps& caps,
94                                                             GrClampType clampType) {
95     if (factory) {
96         return factory->makeXferProcessor(color, coverage, caps, clampType);
97     } else {
98         return GrPorterDuffXPFactory::MakeSrcOverXferProcessor(color, coverage, caps);
99     }
100 }
101 
FromBlendMode(SkBlendMode mode)102 const GrXPFactory* GrXPFactory::FromBlendMode(SkBlendMode mode) {
103     if (SkBlendMode_AsCoeff(mode, nullptr, nullptr)) {
104         const GrXPFactory* result = GrPorterDuffXPFactory::Get(mode);
105         SkASSERT(result);
106         return result;
107     }
108 
109     SkASSERT(GrCustomXfermode::IsSupportedMode(mode));
110     return GrCustomXfermode::Get(mode);
111 }
112 
113 //////////////////////////////////////////////////////////////////////////////
114 
115 using ProgramImpl = GrXferProcessor::ProgramImpl;
116 
117 // This is only called for cases where we are doing LCD coverage and not using in shader blending.
118 // For these cases we assume the the src alpha is 1, thus we can just use the max for the alpha
119 // 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)120 static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder,
121                                     const char* srcCoverage,
122                                     const GrXferProcessor& proc) {
123     if (srcCoverage && proc.isLCD()) {
124         fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);",
125                                  srcCoverage,
126                                  srcCoverage,
127                                  srcCoverage,
128                                  srcCoverage);
129     }
130 }
131 
emitCode(const EmitArgs & args)132 void ProgramImpl::emitCode(const EmitArgs& args) {
133     if (!args.fXP.willReadDstColor()) {
134         adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP);
135         this->emitOutputsForBlendState(args);
136     } else {
137         GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
138         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
139         const char* dstColor = fragBuilder->dstColor();
140 
141         bool needsLocalOutColor = false;
142 
143         if (args.fDstTextureSamplerHandle.isValid()) {
144             if (args.fInputCoverage) {
145                 // We don't think any shaders actually output negative coverage, but just as a
146                 // safety check for floating point precision errors, we compare with <= here. We
147                 // just check the RGB values of the coverage, since the alpha may not have been set
148                 // when using LCD. If we are using single-channel coverage, alpha will be equal to
149                 // RGB anyway.
150                 //
151                 // The discard here also helps for batching text-draws together, which need to read
152                 // from a dst copy for blends. However, this only helps the case where the outer
153                 // bounding boxes of each letter overlap and not two actually parts of the text.
154                 fragBuilder->codeAppendf("if (all(lessThanEqual(%s.rgb, half3(0)))) {"
155                                          "    discard;"
156                                          "}",
157                                          args.fInputCoverage);
158             }
159         } else {
160             needsLocalOutColor = args.fShaderCaps->fRequiresLocalOutputColorForFBFetch;
161         }
162 
163         const char* outColor = "_localColorOut";
164         if (!needsLocalOutColor) {
165             outColor = args.fOutputPrimary;
166         } else {
167             fragBuilder->codeAppendf("half4 %s;", outColor);
168         }
169 
170         this->emitBlendCodeForDstRead(fragBuilder,
171                                       uniformHandler,
172                                       args.fInputColor,
173                                       args.fInputCoverage,
174                                       dstColor,
175                                       outColor,
176                                       args.fOutputSecondary,
177                                       args.fXP);
178         if (needsLocalOutColor) {
179             fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, outColor);
180         }
181     }
182 
183     // Swizzle the fragment shader outputs if necessary.
184     this->emitWriteSwizzle(args.fXPFragBuilder,
185                            args.fWriteSwizzle,
186                            args.fOutputPrimary,
187                            args.fOutputSecondary);
188 }
189 
emitWriteSwizzle(GrGLSLXPFragmentBuilder * x,const skgpu::Swizzle & swizzle,const char * outColor,const char * outColorSecondary) const190 void ProgramImpl::emitWriteSwizzle(GrGLSLXPFragmentBuilder* x,
191                                    const skgpu::Swizzle& swizzle,
192                                    const char* outColor,
193                                    const char* outColorSecondary) const {
194     if (skgpu::Swizzle::RGBA() != swizzle) {
195         x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.asString().c_str());
196         if (outColorSecondary) {
197             x->codeAppendf("%s = %s.%s;",
198                            outColorSecondary,
199                            outColorSecondary,
200                            swizzle.asString().c_str());
201         }
202     }
203 }
204 
setData(const GrGLSLProgramDataManager & pdm,const GrXferProcessor & xp)205 void ProgramImpl::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp) {
206     this->onSetData(pdm, xp);
207 }
208 
DefaultCoverageModulation(GrGLSLXPFragmentBuilder * fragBuilder,const char * srcCoverage,const char * dstColor,const char * outColor,const char * outColorSecondary,const GrXferProcessor & proc)209 void ProgramImpl::DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder,
210                                             const char* srcCoverage,
211                                             const char* dstColor,
212                                             const char* outColor,
213                                             const char* outColorSecondary,
214                                             const GrXferProcessor& proc) {
215     if (srcCoverage) {
216         if (proc.isLCD()) {
217             fragBuilder->codeAppendf("half3 lerpRGB = mix(%s.aaa, %s.aaa, %s.rgb);",
218                                      dstColor,
219                                      outColor,
220                                      srcCoverage);
221         }
222         fragBuilder->codeAppendf("%s = %s * %s + (half4(1.0) - %s) * %s;",
223                                  outColor,
224                                  srcCoverage,
225                                  outColor,
226                                  srcCoverage,
227                                  dstColor);
228         if (proc.isLCD()) {
229             fragBuilder->codeAppendf("%s.a = max(max(lerpRGB.r, lerpRGB.b), lerpRGB.g);", outColor);
230         }
231     }
232 }
233