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