/src/skia/src/gpu/ganesh/GrXferProcessor.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
24 | | GrXferProcessor::GrXferProcessor(ClassID classID) |
25 | | : INHERITED(classID) |
26 | | , fWillReadDstColor(false) |
27 | 45.9k | , fIsLCD(false) {} |
28 | | |
29 | | GrXferProcessor::GrXferProcessor(ClassID classID, bool willReadDstColor, |
30 | | GrProcessorAnalysisCoverage coverage) |
31 | | : INHERITED(classID) |
32 | | , fWillReadDstColor(willReadDstColor) |
33 | 197k | , fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {} |
34 | | |
35 | 0 | bool GrXferProcessor::hasSecondaryOutput() const { |
36 | 0 | if (!this->willReadDstColor()) { |
37 | 0 | return this->onHasSecondaryOutput(); |
38 | 0 | } |
39 | 0 | return false; |
40 | 0 | } |
41 | | |
42 | | void GrXferProcessor::addToKey(const GrShaderCaps& caps, |
43 | | skgpu::KeyBuilder* b, |
44 | | const GrSurfaceOrigin* originIfDstTexture, |
45 | 0 | bool usesInputAttachmentForDstRead) const { |
46 | 0 | uint32_t key = this->willReadDstColor() ? 0x1 : 0x0; |
47 | 0 | if (key) { |
48 | 0 | if (originIfDstTexture) { |
49 | 0 | key |= 0x2; |
50 | 0 | if (kTopLeft_GrSurfaceOrigin == *originIfDstTexture) { |
51 | 0 | key |= 0x4; |
52 | 0 | } |
53 | 0 | if (usesInputAttachmentForDstRead) { |
54 | 0 | key |= 0x8; |
55 | 0 | } |
56 | 0 | } |
57 | 0 | } |
58 | 0 | if (fIsLCD) { |
59 | 0 | key |= 0x10; |
60 | 0 | } |
61 | 0 | b->add32(key); |
62 | 0 | this->onAddToKey(caps, b); |
63 | 0 | } |
64 | | |
65 | | /////////////////////////////////////////////////////////////////////////////// |
66 | | |
67 | | GrXPFactory::AnalysisProperties GrXPFactory::GetAnalysisProperties( |
68 | | const GrXPFactory* factory, |
69 | | const GrProcessorAnalysisColor& color, |
70 | | const GrProcessorAnalysisCoverage& coverage, |
71 | | const GrCaps& caps, |
72 | 326k | GrClampType clampType) { |
73 | 326k | AnalysisProperties result; |
74 | 326k | if (factory) { |
75 | 243k | result = factory->analysisProperties(color, coverage, caps, clampType); |
76 | 243k | } else { |
77 | 83.7k | result = GrPorterDuffXPFactory::SrcOverAnalysisProperties(color, coverage, caps, clampType); |
78 | 83.7k | } |
79 | 326k | if (coverage == GrProcessorAnalysisCoverage::kNone) { |
80 | 233k | result |= AnalysisProperties::kCompatibleWithCoverageAsAlpha; |
81 | 233k | } |
82 | 326k | SkASSERT(!(result & AnalysisProperties::kRequiresDstTexture)); |
83 | 326k | if ((result & AnalysisProperties::kReadsDstInShader) && |
84 | 326k | !caps.shaderCaps()->fDstReadInShaderSupport) { |
85 | 37.1k | result |= AnalysisProperties::kRequiresDstTexture | |
86 | 37.1k | AnalysisProperties::kRequiresNonOverlappingDraws; |
87 | 37.1k | } |
88 | 326k | return result; |
89 | 326k | } |
90 | | |
91 | | sk_sp<const GrXferProcessor> GrXPFactory::MakeXferProcessor(const GrXPFactory* factory, |
92 | | const GrProcessorAnalysisColor& color, |
93 | | GrProcessorAnalysisCoverage coverage, |
94 | | const GrCaps& caps, |
95 | 326k | GrClampType clampType) { |
96 | 326k | if (factory) { |
97 | 243k | return factory->makeXferProcessor(color, coverage, caps, clampType); |
98 | 243k | } else { |
99 | 83.7k | return GrPorterDuffXPFactory::MakeSrcOverXferProcessor(color, coverage, caps); |
100 | 83.7k | } |
101 | 326k | } |
102 | | |
103 | 239k | const GrXPFactory* GrXPFactory::FromBlendMode(SkBlendMode mode) { |
104 | 239k | if (SkBlendMode_AsCoeff(mode, nullptr, nullptr)) { |
105 | 186k | const GrXPFactory* result = GrPorterDuffXPFactory::Get(mode); |
106 | 186k | SkASSERT(result); |
107 | 186k | return result; |
108 | 186k | } |
109 | | |
110 | 52.6k | SkASSERT(GrCustomXfermode::IsSupportedMode(mode)); |
111 | 52.6k | return GrCustomXfermode::Get(mode); |
112 | 239k | } |
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. |
121 | | static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder, |
122 | | const char* srcCoverage, |
123 | 0 | const GrXferProcessor& proc) { |
124 | 0 | if (srcCoverage && proc.isLCD()) { |
125 | 0 | fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);", |
126 | 0 | srcCoverage, |
127 | 0 | srcCoverage, |
128 | 0 | srcCoverage, |
129 | 0 | srcCoverage); |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | 0 | void ProgramImpl::emitCode(const EmitArgs& args) { |
134 | 0 | if (!args.fXP.willReadDstColor()) { |
135 | 0 | adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP); |
136 | 0 | this->emitOutputsForBlendState(args); |
137 | 0 | } else { |
138 | 0 | GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder; |
139 | 0 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
140 | 0 | const char* dstColor = fragBuilder->dstColor(); |
141 | |
|
142 | 0 | bool needsLocalOutColor = false; |
143 | |
|
144 | 0 | if (args.fDstTextureSamplerHandle.isValid()) { |
145 | 0 | 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 | 0 | fragBuilder->codeAppendf("if (all(lessThanEqual(%s.rgb, half3(0)))) {" |
156 | 0 | " discard;" |
157 | 0 | "}", |
158 | 0 | args.fInputCoverage); |
159 | 0 | } |
160 | 0 | } else { |
161 | 0 | needsLocalOutColor = args.fShaderCaps->fRequiresLocalOutputColorForFBFetch; |
162 | 0 | } |
163 | |
|
164 | 0 | const char* outColor = "_localColorOut"; |
165 | 0 | if (!needsLocalOutColor) { |
166 | 0 | outColor = args.fOutputPrimary; |
167 | 0 | } else { |
168 | 0 | fragBuilder->codeAppendf("half4 %s;", outColor); |
169 | 0 | } |
170 | |
|
171 | 0 | this->emitBlendCodeForDstRead(fragBuilder, |
172 | 0 | uniformHandler, |
173 | 0 | args.fInputColor, |
174 | 0 | args.fInputCoverage, |
175 | 0 | dstColor, |
176 | 0 | outColor, |
177 | 0 | args.fOutputSecondary, |
178 | 0 | args.fXP); |
179 | 0 | if (needsLocalOutColor) { |
180 | 0 | fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, outColor); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | // Swizzle the fragment shader outputs if necessary. |
185 | 0 | this->emitWriteSwizzle(args.fXPFragBuilder, |
186 | 0 | args.fWriteSwizzle, |
187 | 0 | args.fOutputPrimary, |
188 | 0 | args.fOutputSecondary); |
189 | 0 | } |
190 | | |
191 | | void ProgramImpl::emitWriteSwizzle(GrGLSLXPFragmentBuilder* x, |
192 | | const skgpu::Swizzle& swizzle, |
193 | | const char* outColor, |
194 | 0 | const char* outColorSecondary) const { |
195 | 0 | if (skgpu::Swizzle::RGBA() != swizzle) { |
196 | 0 | x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.asString().c_str()); |
197 | 0 | if (outColorSecondary) { |
198 | 0 | x->codeAppendf("%s = %s.%s;", |
199 | 0 | outColorSecondary, |
200 | 0 | outColorSecondary, |
201 | 0 | swizzle.asString().c_str()); |
202 | 0 | } |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | 0 | void ProgramImpl::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp) { |
207 | 0 | this->onSetData(pdm, xp); |
208 | 0 | } |
209 | | |
210 | | void ProgramImpl::DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder, |
211 | | const char* srcCoverage, |
212 | | const char* dstColor, |
213 | | const char* outColor, |
214 | | const char* outColorSecondary, |
215 | 0 | const GrXferProcessor& proc) { |
216 | 0 | if (srcCoverage) { |
217 | 0 | if (proc.isLCD()) { |
218 | 0 | fragBuilder->codeAppendf("half3 lerpRGB = mix(%s.aaa, %s.aaa, %s.rgb);", |
219 | 0 | dstColor, |
220 | 0 | outColor, |
221 | 0 | srcCoverage); |
222 | 0 | } |
223 | 0 | fragBuilder->codeAppendf("%s = %s * %s + (half4(1.0) - %s) * %s;", |
224 | 0 | outColor, |
225 | 0 | srcCoverage, |
226 | 0 | outColor, |
227 | 0 | srcCoverage, |
228 | 0 | dstColor); |
229 | 0 | if (proc.isLCD()) { |
230 | 0 | fragBuilder->codeAppendf("%s.a = max(max(lerpRGB.r, lerpRGB.b), lerpRGB.g);", outColor); |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |