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