/src/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2014 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/glsl/GrGLSLShaderBuilder.h" |
9 | | |
10 | | #include "include/sksl/DSL.h" |
11 | | #include "src/gpu/GrShaderCaps.h" |
12 | | #include "src/gpu/GrShaderVar.h" |
13 | | #include "src/gpu/GrSwizzle.h" |
14 | | #include "src/gpu/glsl/GrGLSLBlend.h" |
15 | | #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h" |
16 | | #include "src/gpu/glsl/GrGLSLProgramBuilder.h" |
17 | | #include "src/sksl/dsl/priv/DSLWriter.h" |
18 | | #include "src/sksl/ir/SkSLVarDeclarations.h" |
19 | | |
20 | | GrGLSLShaderBuilder::GrGLSLShaderBuilder(GrGLSLProgramBuilder* program) |
21 | | : fProgramBuilder(program) |
22 | | , fInputs(GrGLSLProgramBuilder::kVarsPerBlock) |
23 | | , fOutputs(GrGLSLProgramBuilder::kVarsPerBlock) |
24 | | , fFeaturesAddedMask(0) |
25 | | , fCodeIndex(kCode) |
26 | | , fFinalized(false) |
27 | 0 | , fTmpVariableCounter(0) { |
28 | | // We push back some placeholder pointers which will later become our header |
29 | 0 | for (int i = 0; i <= kCode; i++) { |
30 | 0 | fShaderStrings.push_back(); |
31 | 0 | } |
32 | |
|
33 | 0 | this->main() = "void main() {"; |
34 | 0 | } |
35 | | |
36 | 0 | void GrGLSLShaderBuilder::declAppend(const GrShaderVar& var) { |
37 | 0 | SkString tempDecl; |
38 | 0 | var.appendDecl(fProgramBuilder->shaderCaps(), &tempDecl); |
39 | 0 | this->codeAppendf("%s;", tempDecl.c_str()); |
40 | 0 | } |
41 | | |
42 | 0 | void GrGLSLShaderBuilder::declareGlobal(const GrShaderVar& v) { |
43 | 0 | v.appendDecl(this->getProgramBuilder()->shaderCaps(), &this->definitions()); |
44 | 0 | this->definitions().append(";"); |
45 | 0 | } |
46 | | |
47 | 0 | SkString GrGLSLShaderBuilder::getMangledFunctionName(const char* baseName) { |
48 | 0 | return fProgramBuilder->nameVariable(/*prefix=*/'\0', baseName); |
49 | 0 | } |
50 | | |
51 | | void GrGLSLShaderBuilder::appendFunctionDecl(GrSLType returnType, |
52 | | const char* mangledName, |
53 | 0 | SkSpan<const GrShaderVar> args) { |
54 | 0 | this->functions().appendf("%s %s(", GrGLSLTypeString(returnType), mangledName); |
55 | 0 | for (size_t i = 0; i < args.size(); ++i) { |
56 | 0 | if (i > 0) { |
57 | 0 | this->functions().append(", "); |
58 | 0 | } |
59 | 0 | args[i].appendDecl(fProgramBuilder->shaderCaps(), &this->functions()); |
60 | 0 | } |
61 | |
|
62 | 0 | this->functions().append(")"); |
63 | 0 | } |
64 | | |
65 | | void GrGLSLShaderBuilder::emitFunction(GrSLType returnType, |
66 | | const char* mangledName, |
67 | | SkSpan<const GrShaderVar> args, |
68 | 0 | const char* body) { |
69 | 0 | this->appendFunctionDecl(returnType, mangledName, args); |
70 | 0 | this->functions().appendf(" {\n" |
71 | 0 | "%s" |
72 | 0 | "}\n\n", body); |
73 | 0 | } |
74 | | |
75 | 0 | void GrGLSLShaderBuilder::emitFunction(const char* declaration, const char* body) { |
76 | 0 | this->functions().appendf("%s {\n" |
77 | 0 | "%s" |
78 | 0 | "}\n\n", declaration, body); |
79 | 0 | } |
80 | | |
81 | | void GrGLSLShaderBuilder::emitFunctionPrototype(GrSLType returnType, |
82 | | const char* mangledName, |
83 | 0 | SkSpan<const GrShaderVar> args) { |
84 | 0 | this->appendFunctionDecl(returnType, mangledName, args); |
85 | 0 | this->functions().append(";\n"); |
86 | 0 | } |
87 | | |
88 | 0 | void GrGLSLShaderBuilder::codeAppend(std::unique_ptr<SkSL::Statement> stmt) { |
89 | 0 | SkASSERT(SkSL::dsl::DSLWriter::CurrentProcessor()); |
90 | 0 | SkASSERT(stmt); |
91 | 0 | this->codeAppend(stmt->description().c_str()); |
92 | 0 | if (stmt->is<SkSL::VarDeclaration>()) { |
93 | 0 | fDeclarations.push_back(std::move(stmt)); |
94 | 0 | } |
95 | 0 | } Unexecuted instantiation: GrGLSLShaderBuilder::codeAppend(std::__1::unique_ptr<SkSL::Statement, std::__1::default_delete<SkSL::Statement> >) Unexecuted instantiation: GrGLSLShaderBuilder::codeAppend(std::__1::unique_ptr<SkSL::Statement, std::__1::default_delete<SkSL::Statement> >) |
96 | | |
97 | 0 | static inline void append_texture_swizzle(SkString* out, GrSwizzle swizzle) { |
98 | 0 | if (swizzle != GrSwizzle::RGBA()) { |
99 | 0 | out->appendf(".%s", swizzle.asString().c_str()); |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | void GrGLSLShaderBuilder::appendTextureLookup(SkString* out, |
104 | | SamplerHandle samplerHandle, |
105 | 0 | const char* coordName) const { |
106 | 0 | const char* sampler = fProgramBuilder->samplerVariable(samplerHandle); |
107 | 0 | out->appendf("sample(%s, %s)", sampler, coordName); |
108 | 0 | append_texture_swizzle(out, fProgramBuilder->samplerSwizzle(samplerHandle)); |
109 | 0 | } |
110 | | |
111 | | void GrGLSLShaderBuilder::appendTextureLookup(SamplerHandle samplerHandle, |
112 | | const char* coordName, |
113 | 0 | GrGLSLColorSpaceXformHelper* colorXformHelper) { |
114 | 0 | SkString lookup; |
115 | 0 | this->appendTextureLookup(&lookup, samplerHandle, coordName); |
116 | 0 | this->appendColorGamutXform(lookup.c_str(), colorXformHelper); |
117 | 0 | } |
118 | | |
119 | | void GrGLSLShaderBuilder::appendTextureLookupAndBlend( |
120 | | const char* dst, |
121 | | SkBlendMode mode, |
122 | | SamplerHandle samplerHandle, |
123 | | const char* coordName, |
124 | 0 | GrGLSLColorSpaceXformHelper* colorXformHelper) { |
125 | 0 | if (!dst) { |
126 | 0 | dst = "half4(1)"; |
127 | 0 | } |
128 | 0 | SkString lookup; |
129 | | // This works around an issue in SwiftShader where the texture lookup is messed up |
130 | | // if we use blend_modulate instead of simply operator * on dst and the sampled result. |
131 | | // At this time it's unknown if the same problem exists for other modes. |
132 | 0 | if (mode == SkBlendMode::kModulate) { |
133 | 0 | this->codeAppend("("); |
134 | 0 | this->appendTextureLookup(&lookup, samplerHandle, coordName); |
135 | 0 | this->appendColorGamutXform(lookup.c_str(), colorXformHelper); |
136 | 0 | this->codeAppendf(" * %s)", dst); |
137 | 0 | } else { |
138 | 0 | this->codeAppendf("%s(", GrGLSLBlend::BlendFuncName(mode)); |
139 | 0 | this->appendTextureLookup(&lookup, samplerHandle, coordName); |
140 | 0 | this->appendColorGamutXform(lookup.c_str(), colorXformHelper); |
141 | 0 | this->codeAppendf(", %s)", dst); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | void GrGLSLShaderBuilder::appendInputLoad(SamplerHandle samplerHandle) { |
146 | 0 | const char* input = fProgramBuilder->inputSamplerVariable(samplerHandle); |
147 | 0 | SkString load; |
148 | 0 | load.appendf("subpassLoad(%s)", input); |
149 | 0 | append_texture_swizzle(&load, fProgramBuilder->inputSamplerSwizzle(samplerHandle)); |
150 | 0 | this->codeAppend(load.c_str()); |
151 | 0 | } |
152 | | |
153 | | void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out, |
154 | | const char* srcColor, |
155 | 0 | GrGLSLColorSpaceXformHelper* colorXformHelper) { |
156 | 0 | if (!colorXformHelper || colorXformHelper->isNoop()) { |
157 | 0 | *out = srcColor; |
158 | 0 | return; |
159 | 0 | } |
160 | | |
161 | 0 | GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler(); |
162 | | |
163 | | // We define up to three helper functions, to keep things clearer. One for the source transfer |
164 | | // function, one for the (inverse) destination transfer function, and one for the gamut xform. |
165 | | // Any combination of these may be present, although some configurations are much more likely. |
166 | |
|
167 | 0 | auto emitTFFunc = [=](const char* name, GrGLSLProgramDataManager::UniformHandle uniform, |
168 | 0 | TFKind kind) { |
169 | 0 | const GrShaderVar gTFArgs[] = { GrShaderVar("x", kHalf_GrSLType) }; |
170 | 0 | const char* coeffs = uniformHandler->getUniformCStr(uniform); |
171 | 0 | SkString body; |
172 | | // Temporaries to make evaluation line readable. We always use the sRGBish names, so the |
173 | | // PQ and HLG math is confusing. |
174 | 0 | body.appendf("half G = %s[0];", coeffs); |
175 | 0 | body.appendf("half A = %s[1];", coeffs); |
176 | 0 | body.appendf("half B = %s[2];", coeffs); |
177 | 0 | body.appendf("half C = %s[3];", coeffs); |
178 | 0 | body.appendf("half D = %s[4];", coeffs); |
179 | 0 | body.appendf("half E = %s[5];", coeffs); |
180 | 0 | body.appendf("half F = %s[6];", coeffs); |
181 | 0 | body.append("half s = sign(x);"); |
182 | 0 | body.append("x = abs(x);"); |
183 | 0 | switch (kind) { |
184 | 0 | case TFKind::sRGBish_TF: |
185 | 0 | body.append("x = (x < D) ? (C * x) + F : pow(A * x + B, G) + E;"); |
186 | 0 | break; |
187 | 0 | case TFKind::PQish_TF: |
188 | 0 | body.append("x = pow(max(A + B * pow(x, C), 0) / (D + E * pow(x, C)), F);"); |
189 | 0 | break; |
190 | 0 | case TFKind::HLGish_TF: |
191 | 0 | body.append("x = (x*A <= 1) ? pow(x*A, B) : exp((x-E)*C) + D; x *= (F+1);"); |
192 | 0 | break; |
193 | 0 | case TFKind::HLGinvish_TF: |
194 | 0 | body.append("x /= (F+1); x = (x <= 1) ? A * pow(x, B) : C * log(x - D) + E;"); |
195 | 0 | break; |
196 | 0 | default: |
197 | 0 | SkASSERT(false); |
198 | 0 | break; |
199 | 0 | } |
200 | 0 | body.append("return s * x;"); |
201 | 0 | SkString funcName = this->getMangledFunctionName(name); |
202 | 0 | this->emitFunction(kHalf_GrSLType, funcName.c_str(), {gTFArgs, SK_ARRAY_COUNT(gTFArgs)}, |
203 | 0 | body.c_str()); |
204 | 0 | return funcName; |
205 | 0 | }; Unexecuted instantiation: GrGLSLShaderBuilder.cpp:GrGLSLShaderBuilder::appendColorGamutXform(SkString*, char const*, GrGLSLColorSpaceXformHelper*)::$_0::operator()(char const*, GrResourceHandle<GrGLSLProgramDataManager::UniformHandleKind>, TFKind) const Unexecuted instantiation: GrGLSLShaderBuilder.cpp:GrGLSLShaderBuilder::appendColorGamutXform(SkString*, char const*, GrGLSLColorSpaceXformHelper*)::$_2::operator()(char const*, GrResourceHandle<GrGLSLProgramDataManager::UniformHandleKind>, TFKind) const |
206 | |
|
207 | 0 | SkString srcTFFuncName; |
208 | 0 | if (colorXformHelper->applySrcTF()) { |
209 | 0 | srcTFFuncName = emitTFFunc("src_tf", colorXformHelper->srcTFUniform(), |
210 | 0 | colorXformHelper->srcTFKind()); |
211 | 0 | } |
212 | |
|
213 | 0 | SkString dstTFFuncName; |
214 | 0 | if (colorXformHelper->applyDstTF()) { |
215 | 0 | dstTFFuncName = emitTFFunc("dst_tf", colorXformHelper->dstTFUniform(), |
216 | 0 | colorXformHelper->dstTFKind()); |
217 | 0 | } |
218 | |
|
219 | 0 | SkString gamutXformFuncName; |
220 | 0 | if (colorXformHelper->applyGamutXform()) { |
221 | 0 | const GrShaderVar gGamutXformArgs[] = { GrShaderVar("color", kHalf4_GrSLType) }; |
222 | 0 | const char* xform = uniformHandler->getUniformCStr(colorXformHelper->gamutXformUniform()); |
223 | 0 | SkString body; |
224 | 0 | body.appendf("color.rgb = (%s * color.rgb);", xform); |
225 | 0 | body.append("return color;"); |
226 | 0 | gamutXformFuncName = this->getMangledFunctionName("gamut_xform"); |
227 | 0 | this->emitFunction(kHalf4_GrSLType, gamutXformFuncName.c_str(), |
228 | 0 | {gGamutXformArgs, SK_ARRAY_COUNT(gGamutXformArgs)}, body.c_str()); |
229 | 0 | } |
230 | | |
231 | | // Now define a wrapper function that applies all the intermediate steps |
232 | 0 | { |
233 | | // Some GPUs require full float to get results that are as accurate as expected/required. |
234 | | // Most GPUs work just fine with half float. Strangely, the GPUs that have this bug |
235 | | // (Mali G series) only require us to promote the type of a few temporaries here -- |
236 | | // the helper functions above can always be written to use half. |
237 | 0 | bool useFloat = fProgramBuilder->shaderCaps()->colorSpaceMathNeedsFloat(); |
238 | |
|
239 | 0 | const GrShaderVar gColorXformArgs[] = { |
240 | 0 | GrShaderVar("color", useFloat ? kFloat4_GrSLType : kHalf4_GrSLType)}; |
241 | 0 | SkString body; |
242 | 0 | if (colorXformHelper->applyUnpremul()) { |
243 | 0 | body.append("color = unpremul(color);"); |
244 | 0 | } |
245 | 0 | if (colorXformHelper->applySrcTF()) { |
246 | 0 | body.appendf("color.r = %s(half(color.r));", srcTFFuncName.c_str()); |
247 | 0 | body.appendf("color.g = %s(half(color.g));", srcTFFuncName.c_str()); |
248 | 0 | body.appendf("color.b = %s(half(color.b));", srcTFFuncName.c_str()); |
249 | 0 | } |
250 | 0 | if (colorXformHelper->applyGamutXform()) { |
251 | 0 | body.appendf("color = %s(half4(color));", gamutXformFuncName.c_str()); |
252 | 0 | } |
253 | 0 | if (colorXformHelper->applyDstTF()) { |
254 | 0 | body.appendf("color.r = %s(half(color.r));", dstTFFuncName.c_str()); |
255 | 0 | body.appendf("color.g = %s(half(color.g));", dstTFFuncName.c_str()); |
256 | 0 | body.appendf("color.b = %s(half(color.b));", dstTFFuncName.c_str()); |
257 | 0 | } |
258 | 0 | if (colorXformHelper->applyPremul()) { |
259 | 0 | body.append("color.rgb *= color.a;"); |
260 | 0 | } |
261 | 0 | body.append("return half4(color);"); |
262 | 0 | SkString colorXformFuncName = this->getMangledFunctionName("color_xform"); |
263 | 0 | this->emitFunction(kHalf4_GrSLType, colorXformFuncName.c_str(), |
264 | 0 | {gColorXformArgs, SK_ARRAY_COUNT(gColorXformArgs)}, body.c_str()); |
265 | 0 | out->appendf("%s(%s)", colorXformFuncName.c_str(), srcColor); |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | void GrGLSLShaderBuilder::appendColorGamutXform(const char* srcColor, |
270 | 0 | GrGLSLColorSpaceXformHelper* colorXformHelper) { |
271 | 0 | SkString xform; |
272 | 0 | this->appendColorGamutXform(&xform, srcColor, colorXformHelper); |
273 | 0 | this->codeAppend(xform.c_str()); |
274 | 0 | } |
275 | | |
276 | 0 | bool GrGLSLShaderBuilder::addFeature(uint32_t featureBit, const char* extensionName) { |
277 | 0 | if (featureBit & fFeaturesAddedMask) { |
278 | 0 | return false; |
279 | 0 | } |
280 | 0 | this->extensions().appendf("#extension %s: require\n", extensionName); |
281 | 0 | fFeaturesAddedMask |= featureBit; |
282 | 0 | return true; |
283 | 0 | } |
284 | | |
285 | 0 | void GrGLSLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const { |
286 | 0 | for (const auto& v : vars.items()) { |
287 | 0 | v.appendDecl(fProgramBuilder->shaderCaps(), out); |
288 | 0 | out->append(";\n"); |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | 0 | void GrGLSLShaderBuilder::addLayoutQualifier(const char* param, InterfaceQualifier interface) { |
293 | 0 | SkASSERT(fProgramBuilder->shaderCaps()->generation() >= k330_GrGLSLGeneration || |
294 | 0 | fProgramBuilder->shaderCaps()->mustEnableAdvBlendEqs()); |
295 | 0 | fLayoutParams[interface].push_back() = param; |
296 | 0 | } Unexecuted instantiation: GrGLSLShaderBuilder::addLayoutQualifier(char const*, GrGLSLShaderBuilder::InterfaceQualifier) Unexecuted instantiation: GrGLSLShaderBuilder::addLayoutQualifier(char const*, GrGLSLShaderBuilder::InterfaceQualifier) |
297 | | |
298 | 0 | void GrGLSLShaderBuilder::compileAndAppendLayoutQualifiers() { |
299 | 0 | static const char* interfaceQualifierNames[] = { |
300 | 0 | "in", |
301 | 0 | "out" |
302 | 0 | }; |
303 | |
|
304 | 0 | for (int interface = 0; interface <= kLastInterfaceQualifier; ++interface) { |
305 | 0 | const SkTArray<SkString>& params = fLayoutParams[interface]; |
306 | 0 | if (params.empty()) { |
307 | 0 | continue; |
308 | 0 | } |
309 | 0 | this->layoutQualifiers().appendf("layout(%s", params[0].c_str()); |
310 | 0 | for (int i = 1; i < params.count(); ++i) { |
311 | 0 | this->layoutQualifiers().appendf(", %s", params[i].c_str()); |
312 | 0 | } |
313 | 0 | this->layoutQualifiers().appendf(") %s;\n", interfaceQualifierNames[interface]); |
314 | 0 | } |
315 | |
|
316 | 0 | static_assert(0 == GrGLSLShaderBuilder::kIn_InterfaceQualifier); |
317 | 0 | static_assert(1 == GrGLSLShaderBuilder::kOut_InterfaceQualifier); |
318 | 0 | static_assert(SK_ARRAY_COUNT(interfaceQualifierNames) == kLastInterfaceQualifier + 1); |
319 | 0 | } |
320 | | |
321 | 0 | void GrGLSLShaderBuilder::finalize(uint32_t visibility) { |
322 | 0 | SkASSERT(!fFinalized); |
323 | 0 | this->compileAndAppendLayoutQualifiers(); |
324 | 0 | SkASSERT(visibility); |
325 | 0 | fProgramBuilder->appendUniformDecls((GrShaderFlags) visibility, &this->uniforms()); |
326 | 0 | this->appendDecls(fInputs, &this->inputs()); |
327 | 0 | this->appendDecls(fOutputs, &this->outputs()); |
328 | 0 | this->onFinalize(); |
329 | | // append the 'footer' to code |
330 | 0 | this->code().append("}"); |
331 | |
|
332 | 0 | for (int i = 0; i <= fCodeIndex; i++) { |
333 | 0 | fCompilerString.append(fShaderStrings[i].c_str(), fShaderStrings[i].size()); |
334 | 0 | } |
335 | |
|
336 | 0 | fFinalized = true; |
337 | 0 | } Unexecuted instantiation: GrGLSLShaderBuilder::finalize(unsigned int) Unexecuted instantiation: GrGLSLShaderBuilder::finalize(unsigned int) |