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