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