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