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 "GrShaderVar.h"
9 #include "GrShaderCaps.h"
10 #include "GrSwizzle.h"
11 #include "glsl/GrGLSLShaderBuilder.h"
12 #include "glsl/GrGLSLColorSpaceXformHelper.h"
13 #include "glsl/GrGLSLProgramBuilder.h"
14 
GrGLSLShaderBuilder(GrGLSLProgramBuilder * program)15 GrGLSLShaderBuilder::GrGLSLShaderBuilder(GrGLSLProgramBuilder* program)
16     : fProgramBuilder(program)
17     , fInputs(GrGLSLProgramBuilder::kVarsPerBlock)
18     , fOutputs(GrGLSLProgramBuilder::kVarsPerBlock)
19     , fFeaturesAddedMask(0)
20     , fCodeIndex(kCode)
21     , fFinalized(false) {
22     // We push back some dummy pointers which will later become our header
23     for (int i = 0; i <= kCode; i++) {
24         fShaderStrings.push_back();
25         fCompilerStrings.push_back(nullptr);
26         fCompilerStringLengths.push_back(0);
27     }
28 
29     this->main() = "void main() {";
30 }
31 
declAppend(const GrShaderVar & var)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 
declareGlobal(const GrShaderVar & v)38 void GrGLSLShaderBuilder::declareGlobal(const GrShaderVar& v) {
39     v.appendDecl(this->getProgramBuilder()->shaderCaps(), &this->definitions());
40     this->definitions().append(";");
41 }
42 
emitFunction(GrSLType returnType,const char * name,int argCnt,const GrShaderVar * args,const char * body,SkString * outName)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(fProgramBuilder->shaderCaps(), 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 
append_texture_swizzle(SkString * out,GrSwizzle swizzle)64 static inline void append_texture_swizzle(SkString* out, GrSwizzle swizzle) {
65     if (swizzle != GrSwizzle::RGBA()) {
66         out->appendf(".%s", swizzle.c_str());
67     }
68 }
69 
appendTextureLookup(SkString * out,SamplerHandle samplerHandle,const char * coordName,GrSLType varyingType) const70 void GrGLSLShaderBuilder::appendTextureLookup(SkString* out,
71                                               SamplerHandle samplerHandle,
72                                               const char* coordName,
73                                               GrSLType varyingType) const {
74     const GrShaderVar& sampler = fProgramBuilder->samplerVariable(samplerHandle);
75     out->appendf("texture(%s, %s)", sampler.c_str(), coordName);
76     append_texture_swizzle(out, fProgramBuilder->samplerSwizzle(samplerHandle));
77 }
78 
appendTextureLookup(SamplerHandle samplerHandle,const char * coordName,GrSLType varyingType,GrGLSLColorSpaceXformHelper * colorXformHelper)79 void GrGLSLShaderBuilder::appendTextureLookup(SamplerHandle samplerHandle,
80                                               const char* coordName,
81                                               GrSLType varyingType,
82                                               GrGLSLColorSpaceXformHelper* colorXformHelper) {
83     if (colorXformHelper && colorXformHelper->isValid()) {
84         // With a color gamut transform, we need to wrap the lookup in another function call
85         SkString lookup;
86         this->appendTextureLookup(&lookup, samplerHandle, coordName, varyingType);
87         this->appendColorGamutXform(lookup.c_str(), colorXformHelper);
88     } else {
89         this->appendTextureLookup(&this->code(), samplerHandle, coordName, varyingType);
90     }
91 }
92 
appendTextureLookupAndModulate(const char * modulation,SamplerHandle samplerHandle,const char * coordName,GrSLType varyingType,GrGLSLColorSpaceXformHelper * colorXformHelper)93 void GrGLSLShaderBuilder::appendTextureLookupAndModulate(
94                                                     const char* modulation,
95                                                     SamplerHandle samplerHandle,
96                                                     const char* coordName,
97                                                     GrSLType varyingType,
98                                                     GrGLSLColorSpaceXformHelper* colorXformHelper) {
99     SkString lookup;
100     this->appendTextureLookup(&lookup, samplerHandle, coordName, varyingType);
101     if (colorXformHelper && colorXformHelper->isValid()) {
102         SkString xform;
103         this->appendColorGamutXform(&xform, lookup.c_str(), colorXformHelper);
104         if (modulation) {
105             this->codeAppendf("%s * %s", modulation, xform.c_str());
106         } else {
107             this->codeAppendf("%s", xform.c_str());
108         }
109     } else {
110         if (modulation) {
111             this->codeAppendf("%s * %s", modulation, lookup.c_str());
112         } else {
113             this->codeAppendf("%s", lookup.c_str());
114         }
115     }
116 }
117 
appendColorGamutXform(SkString * out,const char * srcColor,GrGLSLColorSpaceXformHelper * colorXformHelper)118 void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
119                                                 const char* srcColor,
120                                                 GrGLSLColorSpaceXformHelper* colorXformHelper) {
121     GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler();
122 
123     // We define up to three helper functions, to keep things clearer. One does inverse sRGB,
124     // one does an arbitrary transfer function, and the last does gamut xform. Any combination of
125     // these may be present, although some configurations are much more likely.
126 
127     SkString inverseSrgbFuncName;
128     if (colorXformHelper->applyInverseSRGB()) {
129         static const GrShaderVar gInverseSRGBArgs[] = { GrShaderVar("x", kHalf_GrSLType) };
130         SkString body;
131         body.append("return (x <= 0.0031308) ? (x * 12.92) : (1.055 * pow(x, 0.4166667) - 0.055);");
132         this->emitFunction(kHalf_GrSLType, "inverse_srgb", SK_ARRAY_COUNT(gInverseSRGBArgs),
133                            gInverseSRGBArgs, body.c_str(), &inverseSrgbFuncName);
134 
135     }
136 
137     SkString transferFnFuncName;
138     if (colorXformHelper->applyTransferFn()) {
139         static const GrShaderVar gTransferFnArgs[] = { GrShaderVar("x", kHalf_GrSLType) };
140         const char* coeffs = uniformHandler->getUniformCStr(colorXformHelper->transferFnUniform());
141         SkString body;
142         // Temporaries to make evaluation line readable
143         body.appendf("half G = %s[0];", coeffs);
144         body.appendf("half A = %s[1];", coeffs);
145         body.appendf("half B = %s[2];", coeffs);
146         body.appendf("half C = %s[3];", coeffs);
147         body.appendf("half D = %s[4];", coeffs);
148         body.appendf("half E = %s[5];", coeffs);
149         body.appendf("half F = %s[6];", coeffs);
150         body.append("half s = sign(x);");
151         body.append("x = abs(x);");
152         body.appendf("return s * ((x < D) ? (C * x) + F : pow(A * x + B, G) + E);");
153         this->emitFunction(kHalf_GrSLType, "transfer_fn", SK_ARRAY_COUNT(gTransferFnArgs),
154                            gTransferFnArgs, body.c_str(), &transferFnFuncName);
155     }
156 
157     SkString gamutXformFuncName;
158     if (colorXformHelper->applyGamutXform()) {
159         // Our color is (r, g, b, a), but we want to multiply (r, g, b, 1) by our matrix, then
160         // re-insert the original alpha.
161         static const GrShaderVar gGamutXformArgs[] = { GrShaderVar("color", kHalf4_GrSLType) };
162         const char* xform = uniformHandler->getUniformCStr(colorXformHelper->gamutXformUniform());
163         SkString body;
164         body.appendf("color.rgb = clamp((%s * half4(color.rgb, 1.0)).rgb, 0.0, color.a);", xform);
165         body.append("return color;");
166         this->emitFunction(kHalf4_GrSLType, "gamut_xform", SK_ARRAY_COUNT(gGamutXformArgs),
167                            gGamutXformArgs, body.c_str(), &gamutXformFuncName);
168     }
169 
170     // Now define a wrapper function that applies all the intermediate steps
171     {
172         static const GrShaderVar gColorXformArgs[] = { GrShaderVar("color", kHalf4_GrSLType) };
173         SkString body;
174         if (colorXformHelper->applyInverseSRGB()) {
175             body.appendf("color.r = %s(color.r);", inverseSrgbFuncName.c_str());
176             body.appendf("color.g = %s(color.g);", inverseSrgbFuncName.c_str());
177             body.appendf("color.b = %s(color.b);", inverseSrgbFuncName.c_str());
178         }
179         if (colorXformHelper->applyTransferFn()) {
180             body.appendf("color.r = %s(color.r);", transferFnFuncName.c_str());
181             body.appendf("color.g = %s(color.g);", transferFnFuncName.c_str());
182             body.appendf("color.b = %s(color.b);", transferFnFuncName.c_str());
183         }
184         if (colorXformHelper->applyGamutXform()) {
185             body.appendf("color = %s(color);", gamutXformFuncName.c_str());
186         }
187         body.append("return color;");
188         SkString colorXformFuncName;
189         this->emitFunction(kHalf4_GrSLType, "color_xform", SK_ARRAY_COUNT(gColorXformArgs),
190                            gColorXformArgs, body.c_str(), &colorXformFuncName);
191         out->appendf("%s(%s)", colorXformFuncName.c_str(), srcColor);
192     }
193 }
194 
appendColorGamutXform(const char * srcColor,GrGLSLColorSpaceXformHelper * colorXformHelper)195 void GrGLSLShaderBuilder::appendColorGamutXform(const char* srcColor,
196                                                 GrGLSLColorSpaceXformHelper* colorXformHelper) {
197     SkString xform;
198     this->appendColorGamutXform(&xform, srcColor, colorXformHelper);
199     this->codeAppend(xform.c_str());
200 }
201 
appendTexelFetch(SkString * out,TexelBufferHandle texelBufferHandle,const char * coordExpr) const202 void GrGLSLShaderBuilder::appendTexelFetch(SkString* out,
203                                            TexelBufferHandle texelBufferHandle,
204                                            const char* coordExpr) const {
205     const GrShaderVar& texelBuffer = fProgramBuilder->texelBufferVariable(texelBufferHandle);
206     SkASSERT(fProgramBuilder->shaderCaps()->texelFetchSupport());
207 
208     out->appendf("texelFetch(%s, %s)", texelBuffer.c_str(), coordExpr);
209 }
210 
appendTexelFetch(TexelBufferHandle texelBufferHandle,const char * coordExpr)211 void GrGLSLShaderBuilder::appendTexelFetch(TexelBufferHandle texelBufferHandle,
212                                            const char* coordExpr) {
213     this->appendTexelFetch(&this->code(), texelBufferHandle, coordExpr);
214 }
215 
addFeature(uint32_t featureBit,const char * extensionName)216 bool GrGLSLShaderBuilder::addFeature(uint32_t featureBit, const char* extensionName) {
217     if (featureBit & fFeaturesAddedMask) {
218         return false;
219     }
220     this->extensions().appendf("#extension %s: require\n", extensionName);
221     fFeaturesAddedMask |= featureBit;
222     return true;
223 }
224 
appendDecls(const VarArray & vars,SkString * out) const225 void GrGLSLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const {
226     for (int i = 0; i < vars.count(); ++i) {
227         vars[i].appendDecl(fProgramBuilder->shaderCaps(), out);
228         out->append(";\n");
229     }
230 }
231 
addLayoutQualifier(const char * param,InterfaceQualifier interface)232 void GrGLSLShaderBuilder::addLayoutQualifier(const char* param, InterfaceQualifier interface) {
233     SkASSERT(fProgramBuilder->shaderCaps()->generation() >= k330_GrGLSLGeneration ||
234              fProgramBuilder->shaderCaps()->mustEnableAdvBlendEqs());
235     fLayoutParams[interface].push_back() = param;
236 }
237 
compileAndAppendLayoutQualifiers()238 void GrGLSLShaderBuilder::compileAndAppendLayoutQualifiers() {
239     static const char* interfaceQualifierNames[] = {
240         "in",
241         "out"
242     };
243 
244     for (int interface = 0; interface <= kLastInterfaceQualifier; ++interface) {
245         const SkTArray<SkString>& params = fLayoutParams[interface];
246         if (params.empty()) {
247             continue;
248         }
249         this->layoutQualifiers().appendf("layout(%s", params[0].c_str());
250         for (int i = 1; i < params.count(); ++i) {
251             this->layoutQualifiers().appendf(", %s", params[i].c_str());
252         }
253         this->layoutQualifiers().appendf(") %s;\n", interfaceQualifierNames[interface]);
254     }
255 
256     GR_STATIC_ASSERT(0 == GrGLSLShaderBuilder::kIn_InterfaceQualifier);
257     GR_STATIC_ASSERT(1 == GrGLSLShaderBuilder::kOut_InterfaceQualifier);
258     GR_STATIC_ASSERT(SK_ARRAY_COUNT(interfaceQualifierNames) == kLastInterfaceQualifier + 1);
259 }
260 
finalize(uint32_t visibility)261 void GrGLSLShaderBuilder::finalize(uint32_t visibility) {
262     SkASSERT(!fFinalized);
263     this->versionDecl() = fProgramBuilder->shaderCaps()->versionDeclString();
264     this->compileAndAppendLayoutQualifiers();
265     SkASSERT(visibility);
266     fProgramBuilder->appendUniformDecls((GrShaderFlags) visibility, &this->uniforms());
267     this->appendDecls(fInputs, &this->inputs());
268     this->appendDecls(fOutputs, &this->outputs());
269     this->onFinalize();
270     // append the 'footer' to code
271     this->code().append("}");
272 
273     for (int i = 0; i <= fCodeIndex; i++) {
274         fCompilerStrings[i] = fShaderStrings[i].c_str();
275         fCompilerStringLengths[i] = (int)fShaderStrings[i].size();
276     }
277 
278     fFinalized = true;
279 }
280