blob: 47fa7217333f397d838ee7fd15eaddaa28947a37 [file] [log] [blame]
ethannicholasf789b382016-08-03 12:43:36 -07001/*
2 * Copyright 2016 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 */
Greg Daniel64773e62016-11-22 09:44:03 -05007
ethannicholasf789b382016-08-03 12:43:36 -07008#include "SkSLGLSLCodeGenerator.h"
9
Ethan Nicholas941e7e22016-12-12 15:33:30 -050010#include "SkSLCompiler.h"
ethannicholasf789b382016-08-03 12:43:36 -070011#include "ir/SkSLExpressionStatement.h"
12#include "ir/SkSLExtension.h"
13#include "ir/SkSLIndexExpression.h"
ethannicholas5961bc92016-10-12 06:39:56 -070014#include "ir/SkSLModifiersDeclaration.h"
Ethan Nicholascb670962017-04-20 19:31:52 -040015#include "ir/SkSLNop.h"
ethannicholasf789b382016-08-03 12:43:36 -070016#include "ir/SkSLVariableReference.h"
17
18namespace SkSL {
19
20void GLSLCodeGenerator::write(const char* s) {
21 if (s[0] == 0) {
22 return;
23 }
24 if (fAtLineStart) {
25 for (int i = 0; i < fIndentation; i++) {
Ethan Nicholas9e1138d2016-11-21 10:39:35 -050026 fOut->writeText(" ");
ethannicholasf789b382016-08-03 12:43:36 -070027 }
28 }
Ethan Nicholas9e1138d2016-11-21 10:39:35 -050029 fOut->writeText(s);
ethannicholasf789b382016-08-03 12:43:36 -070030 fAtLineStart = false;
31}
32
33void GLSLCodeGenerator::writeLine(const char* s) {
34 this->write(s);
Ethan Nicholas762466e2017-06-29 10:03:38 -040035 fOut->writeText(fLineEnding);
ethannicholasf789b382016-08-03 12:43:36 -070036 fAtLineStart = true;
37}
38
Ethan Nicholas0df1b042017-03-31 13:56:23 -040039void GLSLCodeGenerator::write(const String& s) {
ethannicholasf789b382016-08-03 12:43:36 -070040 this->write(s.c_str());
41}
42
Ethan Nicholas5b5f0962017-09-11 13:50:14 -070043void GLSLCodeGenerator::write(StringFragment s) {
44 if (!s.fLength) {
45 return;
46 }
47 if (fAtLineStart) {
48 for (int i = 0; i < fIndentation; i++) {
49 fOut->writeText(" ");
50 }
51 }
52 fOut->write(s.fChars, s.fLength);
53 fAtLineStart = false;
54}
55
Ethan Nicholas0df1b042017-03-31 13:56:23 -040056void GLSLCodeGenerator::writeLine(const String& s) {
ethannicholasf789b382016-08-03 12:43:36 -070057 this->writeLine(s.c_str());
58}
59
60void GLSLCodeGenerator::writeLine() {
61 this->writeLine("");
62}
63
64void GLSLCodeGenerator::writeExtension(const Extension& ext) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -070065 this->write("#extension ");
66 this->write(ext.fName);
67 this->writeLine(" : enable");
ethannicholasf789b382016-08-03 12:43:36 -070068}
69
Ethan Nicholasf7b88202017-09-18 14:10:39 -040070bool GLSLCodeGenerator::usesPrecisionModifiers() const {
71 return fProgram.fSettings.fCaps->usesPrecisionModifiers();
72}
73
74String GLSLCodeGenerator::getTypeName(const Type& type) {
75 switch (type.kind()) {
76 case Type::kVector_Kind: {
77 Type component = type.componentType();
78 String result;
79 if (component == *fContext.fFloat_Type || component == *fContext.fHalf_Type) {
80 result = "vec";
81 }
82 else if (component == *fContext.fDouble_Type) {
83 result = "dvec";
84 }
85 else if (component == *fContext.fInt_Type || component == *fContext.fShort_Type) {
86 result = "ivec";
87 }
88 else if (component == *fContext.fUInt_Type || component == *fContext.fUShort_Type) {
89 result = "uvec";
90 }
91 else if (component == *fContext.fBool_Type) {
92 result = "bvec";
93 }
94 else {
95 ABORT("unsupported vector type");
96 }
97 result += to_string(type.columns());
98 return result;
99 }
100 case Type::kMatrix_Kind: {
101 String result;
102 Type component = type.componentType();
103 if (component == *fContext.fFloat_Type || component == *fContext.fHalf_Type) {
104 result = "mat";
105 }
106 else if (component == *fContext.fDouble_Type) {
107 result = "dmat";
108 }
109 else {
110 ABORT("unsupported matrix type");
111 }
112 result += to_string(type.columns());
113 if (type.columns() != type.rows()) {
114 result += "x";
115 result += to_string(type.rows());
116 }
117 return result;
118 }
119 case Type::kArray_Kind: {
120 String result = this->getTypeName(type.componentType()) + "[";
121 if (type.columns() != -1) {
122 result += to_string(type.columns());
123 }
124 result += "]";
125 return result;
126 }
127 case Type::kScalar_Kind: {
128 if (type == *fContext.fHalf_Type) {
129 return "float";
130 }
131 else if (type == *fContext.fShort_Type) {
132 return "int";
133 }
134 else if (type == *fContext.fUShort_Type) {
135 return "uint";
136 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400137 else {
138 return type.name();
139 }
140 break;
141 }
142 default:
143 return type.name();
144 }
145}
146
ethannicholasf789b382016-08-03 12:43:36 -0700147void GLSLCodeGenerator::writeType(const Type& type) {
148 if (type.kind() == Type::kStruct_Kind) {
149 for (const Type* search : fWrittenStructs) {
150 if (*search == type) {
151 // already written
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700152 this->write(type.fName);
ethannicholasf789b382016-08-03 12:43:36 -0700153 return;
154 }
155 }
156 fWrittenStructs.push_back(&type);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700157 this->write("struct ");
158 this->write(type.fName);
159 this->writeLine(" {");
ethannicholasf789b382016-08-03 12:43:36 -0700160 fIndentation++;
161 for (const auto& f : type.fields()) {
ethannicholas5961bc92016-10-12 06:39:56 -0700162 this->writeModifiers(f.fModifiers, false);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -0400163 this->writeTypePrecision(*f.fType);
ethannicholasf789b382016-08-03 12:43:36 -0700164 // sizes (which must be static in structs) are part of the type name here
ethannicholas0730be72016-09-01 07:59:02 -0700165 this->writeType(*f.fType);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700166 this->write(" ");
167 this->write(f.fName);
168 this->writeLine(";");
ethannicholasf789b382016-08-03 12:43:36 -0700169 }
170 fIndentation--;
Ethan Nicholas19671772016-11-28 16:30:17 -0500171 this->write("}");
ethannicholasf789b382016-08-03 12:43:36 -0700172 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400173 this->write(this->getTypeName(type));
ethannicholasf789b382016-08-03 12:43:36 -0700174 }
175}
176
177void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
178 switch (expr.fKind) {
179 case Expression::kBinary_Kind:
180 this->writeBinaryExpression((BinaryExpression&) expr, parentPrecedence);
181 break;
182 case Expression::kBoolLiteral_Kind:
183 this->writeBoolLiteral((BoolLiteral&) expr);
184 break;
185 case Expression::kConstructor_Kind:
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400186 this->writeConstructor((Constructor&) expr, parentPrecedence);
ethannicholasf789b382016-08-03 12:43:36 -0700187 break;
188 case Expression::kIntLiteral_Kind:
189 this->writeIntLiteral((IntLiteral&) expr);
190 break;
191 case Expression::kFieldAccess_Kind:
192 this->writeFieldAccess(((FieldAccess&) expr));
193 break;
194 case Expression::kFloatLiteral_Kind:
195 this->writeFloatLiteral(((FloatLiteral&) expr));
196 break;
197 case Expression::kFunctionCall_Kind:
198 this->writeFunctionCall((FunctionCall&) expr);
199 break;
200 case Expression::kPrefix_Kind:
201 this->writePrefixExpression((PrefixExpression&) expr, parentPrecedence);
202 break;
203 case Expression::kPostfix_Kind:
204 this->writePostfixExpression((PostfixExpression&) expr, parentPrecedence);
205 break;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400206 case Expression::kSetting_Kind:
207 this->writeSetting((Setting&) expr);
208 break;
ethannicholasf789b382016-08-03 12:43:36 -0700209 case Expression::kSwizzle_Kind:
210 this->writeSwizzle((Swizzle&) expr);
211 break;
212 case Expression::kVariableReference_Kind:
213 this->writeVariableReference((VariableReference&) expr);
214 break;
215 case Expression::kTernary_Kind:
216 this->writeTernaryExpression((TernaryExpression&) expr, parentPrecedence);
217 break;
218 case Expression::kIndex_Kind:
219 this->writeIndexExpression((IndexExpression&) expr);
220 break;
221 default:
222 ABORT("unsupported expression: %s", expr.description().c_str());
223 }
224}
225
ethannicholas5961bc92016-10-12 06:39:56 -0700226static bool is_abs(Expression& expr) {
227 if (expr.fKind != Expression::kFunctionCall_Kind) {
228 return false;
229 }
230 return ((FunctionCall&) expr).fFunction.fName == "abs";
231}
232
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500233// turns min(abs(x), y) into ((tmpVar1 = abs(x)) < (tmpVar2 = y) ? tmpVar1 : tmpVar2) to avoid a
ethannicholas5961bc92016-10-12 06:39:56 -0700234// Tegra3 compiler bug.
235void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500236 ASSERT(!fProgram.fSettings.fCaps->canUseMinAndAbsTogether());
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400237 String tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
238 String tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400239 this->fFunctionHeader += String(" ") + this->getTypePrecision(absExpr.fType) +
240 this->getTypeName(absExpr.fType) + " " + tmpVar1 + ";\n";
241 this->fFunctionHeader += String(" ") + this->getTypePrecision(otherExpr.fType) +
242 this->getTypeName(otherExpr.fType) + " " + tmpVar2 + ";\n";
ethannicholas5961bc92016-10-12 06:39:56 -0700243 this->write("((" + tmpVar1 + " = ");
244 this->writeExpression(absExpr, kTopLevel_Precedence);
245 this->write(") < (" + tmpVar2 + " = ");
246 this->writeExpression(otherExpr, kAssignment_Precedence);
247 this->write(") ? " + tmpVar1 + " : " + tmpVar2 + ")");
248}
249
Ethan Nicholas6e6525c2018-01-03 17:03:56 -0500250void GLSLCodeGenerator::writeInverseSqrtHack(const Expression& x) {
251 this->write("(1.0 / sqrt(");
252 this->writeExpression(x, kTopLevel_Precedence);
253 this->write("))");
254}
255
256void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) {
257 String name;
258 if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
259 name = "_determinant2";
260 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
261 fWrittenIntrinsics.insert(name);
262 fExtraFunctions.writeText((
263 "float " + name + "(mat2 m) {"
264 " return m[0][0] * m[1][1] - m[0][1] * m[1][0];"
265 "}"
266 ).c_str());
267 }
268 }
269 else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
270 name = "_determinant3";
271 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
272 fWrittenIntrinsics.insert(name);
273 fExtraFunctions.writeText((
274 "float " + name + "(mat3 m) {"
275 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];"
276 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];"
277 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];"
278 " float b01 = a22 * a11 - a12 * a21;"
279 " float b11 = -a22 * a10 + a12 * a20;"
280 " float b21 = a21 * a10 - a11 * a20;"
281 " return a00 * b01 + a01 * b11 + a02 * b21;"
282 "}"
283 ).c_str());
284 }
285 }
286 else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
287 name = "_determinant3";
288 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
289 fWrittenIntrinsics.insert(name);
290 fExtraFunctions.writeText((
291 "mat4 " + name + "(mat4 m) {"
292 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];"
293 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];"
294 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];"
295 " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];"
296 " float b00 = a00 * a11 - a01 * a10;"
297 " float b01 = a00 * a12 - a02 * a10;"
298 " float b02 = a00 * a13 - a03 * a10;"
299 " float b03 = a01 * a12 - a02 * a11;"
300 " float b04 = a01 * a13 - a03 * a11;"
301 " float b05 = a02 * a13 - a03 * a12;"
302 " float b06 = a20 * a31 - a21 * a30;"
303 " float b07 = a20 * a32 - a22 * a30;"
304 " float b08 = a20 * a33 - a23 * a30;"
305 " float b09 = a21 * a32 - a22 * a31;"
306 " float b10 = a21 * a33 - a23 * a31;"
307 " float b11 = a22 * a33 - a23 * a32;"
308 " return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;"
309 "}"
310 ).c_str());
311 }
312 }
313 else {
314 ASSERT(false);
315 }
316 this->write(name + "(");
317 this->writeExpression(mat, kTopLevel_Precedence);
318 this->write(")");
319}
320
321void GLSLCodeGenerator::writeInverseHack(const Expression& mat) {
322 String name;
323 if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
324 name = "_inverse2";
325 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
326 fWrittenIntrinsics.insert(name);
327 fExtraFunctions.writeText((
328 "mat2 " + name + "(mat2 m) {"
329 " return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / "
330 "(m[0][0] * m[1][1] - m[0][1] * m[1][0]);"
331 "}"
332 ).c_str());
333 }
334 }
335 else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
336 name = "_inverse3";
337 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
338 fWrittenIntrinsics.insert(name);
339 fExtraFunctions.writeText((
340 "mat3 " + name + "(mat3 m) {"
341 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];"
342 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];"
343 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];"
344 " float b01 = a22 * a11 - a12 * a21;"
345 " float b11 = -a22 * a10 + a12 * a20;"
346 " float b21 = a21 * a10 - a11 * a20;"
347 " float det = a00 * b01 + a01 * b11 + a02 * b21;"
348 " return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),"
349 " b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),"
350 " b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;"
351 "}"
352 ).c_str());
353 }
354 }
355 else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
356 name = "_inverse4";
357 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
358 fWrittenIntrinsics.insert(name);
359 fExtraFunctions.writeText((
360 "mat4 " + name + "(mat4 m) {"
361 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];"
362 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];"
363 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];"
364 " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];"
365 " float b00 = a00 * a11 - a01 * a10;"
366 " float b01 = a00 * a12 - a02 * a10;"
367 " float b02 = a00 * a13 - a03 * a10;"
368 " float b03 = a01 * a12 - a02 * a11;"
369 " float b04 = a01 * a13 - a03 * a11;"
370 " float b05 = a02 * a13 - a03 * a12;"
371 " float b06 = a20 * a31 - a21 * a30;"
372 " float b07 = a20 * a32 - a22 * a30;"
373 " float b08 = a20 * a33 - a23 * a30;"
374 " float b09 = a21 * a32 - a22 * a31;"
375 " float b10 = a21 * a33 - a23 * a31;"
376 " float b11 = a22 * a33 - a23 * a32;"
377 " float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - "
378 " b04 * b07 + b05 * b06;"
379 " return mat4("
380 " a11 * b11 - a12 * b10 + a13 * b09,"
381 " a02 * b10 - a01 * b11 - a03 * b09,"
382 " a31 * b05 - a32 * b04 + a33 * b03,"
383 " a22 * b04 - a21 * b05 - a23 * b03,"
384 " a12 * b08 - a10 * b11 - a13 * b07,"
385 " a00 * b11 - a02 * b08 + a03 * b07,"
386 " a32 * b02 - a30 * b05 - a33 * b01,"
387 " a20 * b05 - a22 * b02 + a23 * b01,"
388 " a10 * b10 - a11 * b08 + a13 * b06,"
389 " a01 * b08 - a00 * b10 - a03 * b06,"
390 " a30 * b04 - a31 * b02 + a33 * b00,"
391 " a21 * b02 - a20 * b04 - a23 * b00,"
392 " a11 * b07 - a10 * b09 - a12 * b06,"
393 " a00 * b09 - a01 * b07 + a02 * b06,"
394 " a31 * b01 - a30 * b03 - a32 * b00,"
395 " a20 * b03 - a21 * b01 + a22 * b00) / det;"
396 "}"
397 ).c_str());
398 }
399 }
400 else {
401 ASSERT(false);
402 }
403 this->write(name + "(");
404 this->writeExpression(mat, kTopLevel_Precedence);
405 this->write(")");
406}
407
408void GLSLCodeGenerator::writeTransposeHack(const Expression& mat) {
409 String name = "transpose" + to_string(mat.fType.columns()) + to_string(mat.fType.rows());
410 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
411 fWrittenIntrinsics.insert(name);
412 String type = this->getTypeName(mat.fType);
413 const Type& base = mat.fType.componentType();
414 String transposed = this->getTypeName(base.toCompound(fContext,
415 mat.fType.rows(),
416 mat.fType.columns()));
417 fExtraFunctions.writeText((transposed + " " + name + "(" + type + " m) {\nreturn " +
418 transposed + "(").c_str());
419 const char* separator = "";
420 for (int row = 0; row < mat.fType.rows(); ++row) {
421 for (int column = 0; column < mat.fType.columns(); ++column) {
422 fExtraFunctions.writeText(separator);
423 fExtraFunctions.writeText(("m[" + to_string(column) + "][" + to_string(row) +
424 "]").c_str());
425 separator = ", ";
426 }
427 }
428 fExtraFunctions.writeText("); }");
429 }
430 this->write(name + "(");
431 this->writeExpression(mat, kTopLevel_Precedence);
432 this->write(")");
433}
434
ethannicholasf789b382016-08-03 12:43:36 -0700435void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500436 if (!fProgram.fSettings.fCaps->canUseMinAndAbsTogether() && c.fFunction.fName == "min" &&
437 c.fFunction.fBuiltin) {
ethannicholas5961bc92016-10-12 06:39:56 -0700438 ASSERT(c.fArguments.size() == 2);
439 if (is_abs(*c.fArguments[0])) {
440 this->writeMinAbsHack(*c.fArguments[0], *c.fArguments[1]);
441 return;
442 }
443 if (is_abs(*c.fArguments[1])) {
444 // note that this violates the GLSL left-to-right evaluation semantics. I doubt it will
445 // ever end up mattering, but it's worth calling out.
446 this->writeMinAbsHack(*c.fArguments[1], *c.fArguments[0]);
447 return;
448 }
449 }
Florin Malita3b30c4f2017-08-08 15:47:35 -0400450 if (!fProgram.fSettings.fCaps->canUseFractForNegativeValues() && c.fFunction.fName == "fract" &&
451 c.fFunction.fBuiltin) {
452 ASSERT(c.fArguments.size() == 1);
453
454 this->write("(0.5 - sign(");
455 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
456 this->write(") * (0.5 - fract(abs(");
457 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
458 this->write("))))");
459
460 return;
461 }
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500462 if (fProgram.fSettings.fCaps->mustForceNegatedAtanParamToFloat() &&
463 c.fFunction.fName == "atan" &&
464 c.fFunction.fBuiltin && c.fArguments.size() == 2 &&
ethannicholasddb37d62016-10-20 09:54:00 -0700465 c.fArguments[1]->fKind == Expression::kPrefix_Kind) {
ethannicholasad146f62016-10-14 06:40:02 -0700466 const PrefixExpression& p = (PrefixExpression&) *c.fArguments[1];
467 if (p.fOperator == Token::MINUS) {
468 this->write("atan(");
469 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
470 this->write(", -1.0 * ");
471 this->writeExpression(*p.fOperand, kMultiplicative_Precedence);
472 this->write(")");
473 return;
474 }
475 }
Ethan Nicholas6e6525c2018-01-03 17:03:56 -0500476 if (c.fFunction.fBuiltin && c.fFunction.fName == "determinant" &&
477 fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
478 ASSERT(c.fArguments.size() == 1);
479 this->writeDeterminantHack(*c.fArguments[0]);
480 return;
481 }
482 if (c.fFunction.fBuiltin && c.fFunction.fName == "inverse" &&
483 fProgram.fSettings.fCaps->generation() < k140_GrGLSLGeneration) {
484 ASSERT(c.fArguments.size() == 1);
485 this->writeInverseHack(*c.fArguments[0]);
486 return;
487 }
488 if (c.fFunction.fBuiltin && c.fFunction.fName == "inverseSqrt" &&
489 fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
490 ASSERT(c.fArguments.size() == 1);
491 this->writeInverseSqrtHack(*c.fArguments[0]);
492 return;
493 }
494 if (c.fFunction.fBuiltin && c.fFunction.fName == "transpose" &&
495 fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
496 ASSERT(c.fArguments.size() == 1);
497 this->writeTransposeHack(*c.fArguments[0]);
498 return;
499 }
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500500 if (!fFoundDerivatives && (c.fFunction.fName == "dFdx" || c.fFunction.fName == "dFdy") &&
501 c.fFunction.fBuiltin && fProgram.fSettings.fCaps->shaderDerivativeExtensionString()) {
502 ASSERT(fProgram.fSettings.fCaps->shaderDerivativeSupport());
Ethan Nicholas9e1138d2016-11-21 10:39:35 -0500503 fHeader.writeText("#extension ");
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500504 fHeader.writeText(fProgram.fSettings.fCaps->shaderDerivativeExtensionString());
Ethan Nicholas9e1138d2016-11-21 10:39:35 -0500505 fHeader.writeText(" : require\n");
ethannicholasddb37d62016-10-20 09:54:00 -0700506 fFoundDerivatives = true;
507 }
Brian Osman8a83ca42018-02-12 14:32:17 -0500508 bool isTextureFunctionWithBias = false;
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500509 if (c.fFunction.fName == "texture" && c.fFunction.fBuiltin) {
510 const char* dim = "";
511 bool proj = false;
512 switch (c.fArguments[0]->fType.dimensions()) {
513 case SpvDim1D:
514 dim = "1D";
Brian Osman8a83ca42018-02-12 14:32:17 -0500515 isTextureFunctionWithBias = true;
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500516 if (c.fArguments[1]->fType == *fContext.fFloat_Type) {
517 proj = false;
518 } else {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400519 ASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500520 proj = true;
521 }
522 break;
523 case SpvDim2D:
524 dim = "2D";
Brian Osmanc35d7ea2018-02-21 12:00:14 -0500525 if (c.fArguments[0]->fType != *fContext.fSamplerExternalOES_Type) {
526 isTextureFunctionWithBias = true;
527 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400528 if (c.fArguments[1]->fType == *fContext.fFloat2_Type) {
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500529 proj = false;
530 } else {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400531 ASSERT(c.fArguments[1]->fType == *fContext.fFloat3_Type);
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500532 proj = true;
533 }
534 break;
535 case SpvDim3D:
536 dim = "3D";
Brian Osman8a83ca42018-02-12 14:32:17 -0500537 isTextureFunctionWithBias = true;
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400538 if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500539 proj = false;
540 } else {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400541 ASSERT(c.fArguments[1]->fType == *fContext.fFloat4_Type);
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500542 proj = true;
543 }
544 break;
545 case SpvDimCube:
546 dim = "Cube";
Brian Osman8a83ca42018-02-12 14:32:17 -0500547 isTextureFunctionWithBias = true;
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500548 proj = false;
549 break;
550 case SpvDimRect:
551 dim = "Rect";
552 proj = false;
553 break;
554 case SpvDimBuffer:
555 ASSERT(false); // doesn't exist
556 dim = "Buffer";
557 proj = false;
558 break;
559 case SpvDimSubpassData:
560 ASSERT(false); // doesn't exist
561 dim = "SubpassData";
562 proj = false;
563 break;
564 }
565 this->write("texture");
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500566 if (fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500567 this->write(dim);
568 }
569 if (proj) {
570 this->write("Proj");
571 }
572
573 } else {
574 this->write(c.fFunction.fName);
575 }
576 this->write("(");
ethannicholasf789b382016-08-03 12:43:36 -0700577 const char* separator = "";
578 for (const auto& arg : c.fArguments) {
579 this->write(separator);
580 separator = ", ";
581 this->writeExpression(*arg, kSequence_Precedence);
582 }
Brian Osman8a83ca42018-02-12 14:32:17 -0500583 if (fProgram.fSettings.fSharpenTextures && isTextureFunctionWithBias) {
584 this->write(", -0.5");
585 }
ethannicholasf789b382016-08-03 12:43:36 -0700586 this->write(")");
587}
588
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400589void GLSLCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
590 if (c.fArguments.size() == 1 &&
591 this->getTypeName(c.fType) == this->getTypeName(c.fArguments[0]->fType)) {
592 // in cases like half(float), they're different types as far as SkSL is concerned but the
593 // same type as far as GLSL is concerned. We avoid a redundant float(float) by just writing
594 // out the inner expression here.
595 this->writeExpression(*c.fArguments[0], parentPrecedence);
596 return;
597 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400598 this->writeType(c.fType);
599 this->write("(");
ethannicholasf789b382016-08-03 12:43:36 -0700600 const char* separator = "";
601 for (const auto& arg : c.fArguments) {
602 this->write(separator);
603 separator = ", ";
604 this->writeExpression(*arg, kSequence_Precedence);
605 }
606 this->write(")");
607}
608
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500609void GLSLCodeGenerator::writeFragCoord() {
Brian Osmancd3261a2018-01-16 13:52:29 +0000610 if (!fProgram.fSettings.fCaps->canUseFragCoord()) {
Brian Salomondba65f92018-01-22 08:43:38 -0500611 if (!fSetupFragCoordWorkaround) {
612 const char* precision = usesPrecisionModifiers() ? "highp " : "";
613 fFunctionHeader += precision;
614 fFunctionHeader += " float sk_FragCoord_InvW = 1. / sk_FragCoord_Workaround.w;\n";
615 fFunctionHeader += precision;
616 fFunctionHeader += " vec4 sk_FragCoord_Resolved = "
617 "vec4(sk_FragCoord_Workaround.xyz * sk_FragCoord_InvW, sk_FragCoord_InvW);\n";
618 // Ensure that we get exact .5 values for x and y.
619 fFunctionHeader += " sk_FragCoord_Resolved.xy = floor(sk_FragCoord_Resolved.xy) + "
620 "vec2(.5);\n";
621 fSetupFragCoordWorkaround = true;
622 }
623 this->write("sk_FragCoord_Resolved");
Brian Osmancd3261a2018-01-16 13:52:29 +0000624 return;
625 }
626
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500627 // We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
628 // to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
629 // declaration varies in earlier GLSL specs. So it is simpler to omit it.
630 if (!fProgram.fSettings.fFlipY) {
631 this->write("gl_FragCoord");
632 } else if (const char* extension =
633 fProgram.fSettings.fCaps->fragCoordConventionsExtensionString()) {
634 if (!fSetupFragPositionGlobal) {
635 if (fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
636 fHeader.writeText("#extension ");
637 fHeader.writeText(extension);
638 fHeader.writeText(" : require\n");
639 }
640 fHeader.writeText("layout(origin_upper_left) in vec4 gl_FragCoord;\n");
641 fSetupFragPositionGlobal = true;
Greg Daniele8e4a3e2016-12-12 17:20:42 +0000642 }
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500643 this->write("gl_FragCoord");
Greg Daniele8e4a3e2016-12-12 17:20:42 +0000644 } else {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500645 if (!fSetupFragPositionGlobal) {
646 // The Adreno compiler seems to be very touchy about access to "gl_FragCoord".
647 // Accessing glFragCoord.zw can cause a program to fail to link. Additionally,
648 // depending on the surrounding code, accessing .xy with a uniform involved can
Brian Osmancd3261a2018-01-16 13:52:29 +0000649 // do the same thing. Copying gl_FragCoord.xy into a temp float2 beforehand
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500650 // (and only accessing .xy) seems to "fix" things.
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400651 const char* precision = usesPrecisionModifiers() ? "highp " : "";
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500652 fHeader.writeText("uniform ");
653 fHeader.writeText(precision);
654 fHeader.writeText("float " SKSL_RTHEIGHT_NAME ";\n");
655 fSetupFragPositionGlobal = true;
656 }
657 if (!fSetupFragPositionLocal) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400658 const char* precision = usesPrecisionModifiers() ? "highp " : "";
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500659 fFunctionHeader += precision;
660 fFunctionHeader += " vec2 _sktmpCoord = gl_FragCoord.xy;\n";
661 fFunctionHeader += precision;
662 fFunctionHeader += " vec4 sk_FragCoord = vec4(_sktmpCoord.x, " SKSL_RTHEIGHT_NAME
663 " - _sktmpCoord.y, 1.0, 1.0);\n";
664 fSetupFragPositionLocal = true;
665 }
666 this->write("sk_FragCoord");
667 }
668}
669
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500670void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
671 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
672 case SK_FRAGCOLOR_BUILTIN:
673 if (fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput()) {
674 this->write("sk_FragColor");
675 } else {
676 this->write("gl_FragColor");
677 }
678 break;
679 case SK_FRAGCOORD_BUILTIN:
680 this->writeFragCoord();
681 break;
Ethan Nicholasa51740c2017-02-07 14:53:32 -0500682 case SK_VERTEXID_BUILTIN:
683 this->write("gl_VertexID");
684 break;
Chris Dalton8580d512017-10-14 22:12:33 -0600685 case SK_INSTANCEID_BUILTIN:
686 this->write("gl_InstanceID");
687 break;
Ethan Nicholas67d64602017-02-09 10:15:25 -0500688 case SK_CLIPDISTANCE_BUILTIN:
689 this->write("gl_ClipDistance");
690 break;
Ethan Nicholas52cad152017-02-16 16:37:32 -0500691 case SK_IN_BUILTIN:
692 this->write("gl_in");
693 break;
694 case SK_INVOCATIONID_BUILTIN:
695 this->write("gl_InvocationID");
696 break;
Ethan Nicholaseab2baa2018-04-13 15:16:27 -0400697 case SK_LASTFRAGCOLOR_BUILTIN:
698 this->write(fProgram.fSettings.fCaps->fbFetchColorName());
699 break;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500700 default:
701 this->write(ref.fVariable.fName);
ethannicholas5961bc92016-10-12 06:39:56 -0700702 }
ethannicholasf789b382016-08-03 12:43:36 -0700703}
704
705void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
706 this->writeExpression(*expr.fBase, kPostfix_Precedence);
707 this->write("[");
708 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
709 this->write("]");
710}
711
Brian Osmancd3261a2018-01-16 13:52:29 +0000712bool is_sk_position(const FieldAccess& f) {
713 return "sk_Position" == f.fBase->fType.fields()[f.fFieldIndex].fName;
714}
715
ethannicholasf789b382016-08-03 12:43:36 -0700716void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
717 if (f.fOwnerKind == FieldAccess::kDefault_OwnerKind) {
718 this->writeExpression(*f.fBase, kPostfix_Precedence);
719 this->write(".");
720 }
Ethan Nicholas67d64602017-02-09 10:15:25 -0500721 switch (f.fBase->fType.fields()[f.fFieldIndex].fModifiers.fLayout.fBuiltin) {
722 case SK_CLIPDISTANCE_BUILTIN:
723 this->write("gl_ClipDistance");
724 break;
725 default:
Ethan Nicholasbed683a2017-09-26 14:23:59 -0400726 StringFragment name = f.fBase->fType.fields()[f.fFieldIndex].fName;
727 if (name == "sk_Position") {
728 this->write("gl_Position");
729 } else if (name == "sk_PointSize") {
730 this->write("gl_PointSize");
731 } else {
732 this->write(f.fBase->fType.fields()[f.fFieldIndex].fName);
733 }
Ethan Nicholas67d64602017-02-09 10:15:25 -0500734 }
ethannicholasf789b382016-08-03 12:43:36 -0700735}
736
737void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
738 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
739 this->write(".");
740 for (int c : swizzle.fComponents) {
741 this->write(&("x\0y\0z\0w\0"[c * 2]));
742 }
743}
744
Ethan Nicholas762466e2017-06-29 10:03:38 -0400745GLSLCodeGenerator::Precedence GLSLCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
ethannicholasf789b382016-08-03 12:43:36 -0700746 switch (op) {
747 case Token::STAR: // fall through
748 case Token::SLASH: // fall through
749 case Token::PERCENT: return GLSLCodeGenerator::kMultiplicative_Precedence;
750 case Token::PLUS: // fall through
751 case Token::MINUS: return GLSLCodeGenerator::kAdditive_Precedence;
752 case Token::SHL: // fall through
753 case Token::SHR: return GLSLCodeGenerator::kShift_Precedence;
754 case Token::LT: // fall through
755 case Token::GT: // fall through
756 case Token::LTEQ: // fall through
757 case Token::GTEQ: return GLSLCodeGenerator::kRelational_Precedence;
758 case Token::EQEQ: // fall through
759 case Token::NEQ: return GLSLCodeGenerator::kEquality_Precedence;
760 case Token::BITWISEAND: return GLSLCodeGenerator::kBitwiseAnd_Precedence;
761 case Token::BITWISEXOR: return GLSLCodeGenerator::kBitwiseXor_Precedence;
762 case Token::BITWISEOR: return GLSLCodeGenerator::kBitwiseOr_Precedence;
763 case Token::LOGICALAND: return GLSLCodeGenerator::kLogicalAnd_Precedence;
764 case Token::LOGICALXOR: return GLSLCodeGenerator::kLogicalXor_Precedence;
765 case Token::LOGICALOR: return GLSLCodeGenerator::kLogicalOr_Precedence;
766 case Token::EQ: // fall through
767 case Token::PLUSEQ: // fall through
768 case Token::MINUSEQ: // fall through
769 case Token::STAREQ: // fall through
770 case Token::SLASHEQ: // fall through
771 case Token::PERCENTEQ: // fall through
772 case Token::SHLEQ: // fall through
773 case Token::SHREQ: // fall through
774 case Token::LOGICALANDEQ: // fall through
775 case Token::LOGICALXOREQ: // fall through
776 case Token::LOGICALOREQ: // fall through
777 case Token::BITWISEANDEQ: // fall through
778 case Token::BITWISEXOREQ: // fall through
779 case Token::BITWISEOREQ: return GLSLCodeGenerator::kAssignment_Precedence;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400780 case Token::COMMA: return GLSLCodeGenerator::kSequence_Precedence;
ethannicholasf789b382016-08-03 12:43:36 -0700781 default: ABORT("unsupported binary operator");
782 }
783}
784
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400785void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
ethannicholasf789b382016-08-03 12:43:36 -0700786 Precedence parentPrecedence) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400787 Precedence precedence = GetBinaryPrecedence(b.fOperator);
ethannicholasf789b382016-08-03 12:43:36 -0700788 if (precedence >= parentPrecedence) {
789 this->write("(");
790 }
Brian Osmancd3261a2018-01-16 13:52:29 +0000791 bool positionWorkaround = Compiler::IsAssignment(b.fOperator) &&
792 Expression::kFieldAccess_Kind == b.fLeft->fKind &&
793 is_sk_position((FieldAccess&) *b.fLeft) &&
794 !strstr(b.fRight->description().c_str(), "sk_RTAdjust") &&
795 !fProgram.fSettings.fCaps->canUseFragCoord();
796 if (positionWorkaround) {
797 this->write("sk_FragCoord_Workaround = (");
798 }
ethannicholasf789b382016-08-03 12:43:36 -0700799 this->writeExpression(*b.fLeft, precedence);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700800 this->write(" ");
801 this->write(Compiler::OperatorName(b.fOperator));
802 this->write(" ");
ethannicholasf789b382016-08-03 12:43:36 -0700803 this->writeExpression(*b.fRight, precedence);
Brian Osmancd3261a2018-01-16 13:52:29 +0000804 if (positionWorkaround) {
805 this->write(")");
806 }
ethannicholasf789b382016-08-03 12:43:36 -0700807 if (precedence >= parentPrecedence) {
808 this->write(")");
809 }
810}
811
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400812void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
ethannicholasf789b382016-08-03 12:43:36 -0700813 Precedence parentPrecedence) {
814 if (kTernary_Precedence >= parentPrecedence) {
815 this->write("(");
816 }
817 this->writeExpression(*t.fTest, kTernary_Precedence);
818 this->write(" ? ");
819 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
820 this->write(" : ");
821 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
822 if (kTernary_Precedence >= parentPrecedence) {
823 this->write(")");
824 }
825}
826
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400827void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p,
ethannicholasf789b382016-08-03 12:43:36 -0700828 Precedence parentPrecedence) {
829 if (kPrefix_Precedence >= parentPrecedence) {
830 this->write("(");
831 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700832 this->write(Compiler::OperatorName(p.fOperator));
ethannicholasf789b382016-08-03 12:43:36 -0700833 this->writeExpression(*p.fOperand, kPrefix_Precedence);
834 if (kPrefix_Precedence >= parentPrecedence) {
835 this->write(")");
836 }
837}
838
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400839void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p,
ethannicholasf789b382016-08-03 12:43:36 -0700840 Precedence parentPrecedence) {
841 if (kPostfix_Precedence >= parentPrecedence) {
842 this->write("(");
843 }
844 this->writeExpression(*p.fOperand, kPostfix_Precedence);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700845 this->write(Compiler::OperatorName(p.fOperator));
ethannicholasf789b382016-08-03 12:43:36 -0700846 if (kPostfix_Precedence >= parentPrecedence) {
847 this->write(")");
848 }
849}
850
851void GLSLCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
852 this->write(b.fValue ? "true" : "false");
853}
854
855void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
ethannicholas5961bc92016-10-12 06:39:56 -0700856 if (i.fType == *fContext.fUInt_Type) {
857 this->write(to_string(i.fValue & 0xffffffff) + "u");
Ethan Nicholas58d56482017-12-19 09:29:22 -0500858 } else if (i.fType == *fContext.fUShort_Type) {
859 this->write(to_string(i.fValue & 0xffff) + "u");
860 } else {
ethannicholas5961bc92016-10-12 06:39:56 -0700861 this->write(to_string((int32_t) i.fValue));
862 }
ethannicholasf789b382016-08-03 12:43:36 -0700863}
864
865void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
866 this->write(to_string(f.fValue));
867}
868
Ethan Nicholas762466e2017-06-29 10:03:38 -0400869void GLSLCodeGenerator::writeSetting(const Setting& s) {
870 ABORT("internal error; setting was not folded to a constant during compilation\n");
871}
872
ethannicholasf789b382016-08-03 12:43:36 -0700873void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400874 this->writeTypePrecision(f.fDeclaration.fReturnType);
ethannicholasf789b382016-08-03 12:43:36 -0700875 this->writeType(f.fDeclaration.fReturnType);
876 this->write(" " + f.fDeclaration.fName + "(");
877 const char* separator = "";
878 for (const auto& param : f.fDeclaration.fParameters) {
879 this->write(separator);
880 separator = ", ";
ethannicholas5961bc92016-10-12 06:39:56 -0700881 this->writeModifiers(param->fModifiers, false);
882 std::vector<int> sizes;
883 const Type* type = &param->fType;
884 while (type->kind() == Type::kArray_Kind) {
885 sizes.push_back(type->columns());
886 type = &type->componentType();
887 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400888 this->writeTypePrecision(*type);
ethannicholas5961bc92016-10-12 06:39:56 -0700889 this->writeType(*type);
ethannicholasf789b382016-08-03 12:43:36 -0700890 this->write(" " + param->fName);
ethannicholas5961bc92016-10-12 06:39:56 -0700891 for (int s : sizes) {
892 if (s <= 0) {
893 this->write("[]");
894 } else {
895 this->write("[" + to_string(s) + "]");
896 }
897 }
ethannicholasf789b382016-08-03 12:43:36 -0700898 }
ethannicholas5961bc92016-10-12 06:39:56 -0700899 this->writeLine(") {");
900
901 fFunctionHeader = "";
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400902 OutputStream* oldOut = fOut;
903 StringStream buffer;
ethannicholas5961bc92016-10-12 06:39:56 -0700904 fOut = &buffer;
905 fIndentation++;
Ethan Nicholascb670962017-04-20 19:31:52 -0400906 this->writeStatements(((Block&) *f.fBody).fStatements);
ethannicholas5961bc92016-10-12 06:39:56 -0700907 fIndentation--;
908 this->writeLine("}");
909
910 fOut = oldOut;
911 this->write(fFunctionHeader);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400912 this->write(buffer.str());
ethannicholasf789b382016-08-03 12:43:36 -0700913}
914
Greg Daniel64773e62016-11-22 09:44:03 -0500915void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
ethannicholas5961bc92016-10-12 06:39:56 -0700916 bool globalContext) {
Brian Salomonf9f45122016-11-29 11:59:17 -0500917 if (modifiers.fFlags & Modifiers::kFlat_Flag) {
918 this->write("flat ");
919 }
ethannicholas5961bc92016-10-12 06:39:56 -0700920 if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
921 this->write("noperspective ");
922 }
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400923 String layout = modifiers.fLayout.description();
Ethan Nicholas8da9e942017-03-09 16:35:09 -0500924 if (layout.size()) {
925 this->write(layout + " ");
926 }
Brian Salomonf9f45122016-11-29 11:59:17 -0500927 if (modifiers.fFlags & Modifiers::kReadOnly_Flag) {
928 this->write("readonly ");
929 }
930 if (modifiers.fFlags & Modifiers::kWriteOnly_Flag) {
931 this->write("writeonly ");
932 }
933 if (modifiers.fFlags & Modifiers::kCoherent_Flag) {
934 this->write("coherent ");
935 }
936 if (modifiers.fFlags & Modifiers::kVolatile_Flag) {
937 this->write("volatile ");
938 }
939 if (modifiers.fFlags & Modifiers::kRestrict_Flag) {
940 this->write("restrict ");
ethannicholas5961bc92016-10-12 06:39:56 -0700941 }
Greg Daniel64773e62016-11-22 09:44:03 -0500942 if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
ethannicholas5961bc92016-10-12 06:39:56 -0700943 (modifiers.fFlags & Modifiers::kOut_Flag)) {
944 this->write("inout ");
945 } else if (modifiers.fFlags & Modifiers::kIn_Flag) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500946 if (globalContext &&
947 fProgram.fSettings.fCaps->generation() < GrGLSLGeneration::k130_GrGLSLGeneration) {
ethannicholas5961bc92016-10-12 06:39:56 -0700948 this->write(fProgramKind == Program::kVertex_Kind ? "attribute "
949 : "varying ");
950 } else {
951 this->write("in ");
952 }
953 } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500954 if (globalContext &&
955 fProgram.fSettings.fCaps->generation() < GrGLSLGeneration::k130_GrGLSLGeneration) {
ethannicholas5961bc92016-10-12 06:39:56 -0700956 this->write("varying ");
957 } else {
958 this->write("out ");
959 }
960 }
961 if (modifiers.fFlags & Modifiers::kUniform_Flag) {
962 this->write("uniform ");
963 }
964 if (modifiers.fFlags & Modifiers::kConst_Flag) {
965 this->write("const ");
966 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400967 if (usesPrecisionModifiers()) {
ethannicholas5961bc92016-10-12 06:39:56 -0700968 if (modifiers.fFlags & Modifiers::kLowp_Flag) {
969 this->write("lowp ");
970 }
971 if (modifiers.fFlags & Modifiers::kMediump_Flag) {
972 this->write("mediump ");
973 }
974 if (modifiers.fFlags & Modifiers::kHighp_Flag) {
975 this->write("highp ");
976 }
977 }
ethannicholasf789b382016-08-03 12:43:36 -0700978}
979
980void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
Ethan Nicholas52cad152017-02-16 16:37:32 -0500981 if (intf.fTypeName == "sk_PerVertex") {
ethannicholasf789b382016-08-03 12:43:36 -0700982 return;
983 }
ethannicholas5961bc92016-10-12 06:39:56 -0700984 this->writeModifiers(intf.fVariable.fModifiers, true);
Ethan Nicholas50afc172017-02-16 14:49:57 -0500985 this->writeLine(intf.fTypeName + " {");
ethannicholasf789b382016-08-03 12:43:36 -0700986 fIndentation++;
Ethan Nicholas50afc172017-02-16 14:49:57 -0500987 const Type* structType = &intf.fVariable.fType;
988 while (structType->kind() == Type::kArray_Kind) {
989 structType = &structType->componentType();
990 }
991 for (const auto& f : structType->fields()) {
ethannicholas5961bc92016-10-12 06:39:56 -0700992 this->writeModifiers(f.fModifiers, false);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -0400993 this->writeTypePrecision(*f.fType);
ethannicholas0730be72016-09-01 07:59:02 -0700994 this->writeType(*f.fType);
ethannicholasf789b382016-08-03 12:43:36 -0700995 this->writeLine(" " + f.fName + ";");
996 }
997 fIndentation--;
Ethan Nicholas50afc172017-02-16 14:49:57 -0500998 this->write("}");
999 if (intf.fInstanceName.size()) {
1000 this->write(" ");
1001 this->write(intf.fInstanceName);
1002 for (const auto& size : intf.fSizes) {
1003 this->write("[");
1004 if (size) {
1005 this->writeExpression(*size, kTopLevel_Precedence);
1006 }
1007 this->write("]");
1008 }
1009 }
1010 this->writeLine(";");
ethannicholasf789b382016-08-03 12:43:36 -07001011}
1012
Ethan Nicholas762466e2017-06-29 10:03:38 -04001013void GLSLCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
1014 this->writeExpression(value, kTopLevel_Precedence);
1015}
1016
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001017const char* GLSLCodeGenerator::getTypePrecision(const Type& type) {
1018 if (usesPrecisionModifiers()) {
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001019 switch (type.kind()) {
1020 case Type::kScalar_Kind:
Chris Daltonc2d0dd62018-03-07 07:46:10 -07001021 if (type == *fContext.fShort_Type || type == *fContext.fUShort_Type) {
1022 if (fProgram.fSettings.fForceHighPrecision ||
1023 fProgram.fSettings.fCaps->incompleteShortIntPrecision()) {
1024 return "highp ";
1025 }
1026 return "mediump ";
1027 }
1028 if (type == *fContext.fHalf_Type) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001029 return fProgram.fSettings.fForceHighPrecision ? "highp " : "mediump ";
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001030 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001031 if (type == *fContext.fFloat_Type || type == *fContext.fInt_Type ||
1032 type == *fContext.fUInt_Type) {
1033 return "highp ";
1034 }
1035 return "";
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001036 case Type::kVector_Kind: // fall through
1037 case Type::kMatrix_Kind:
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001038 return this->getTypePrecision(type.componentType());
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001039 default:
1040 break;
1041 }
1042 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001043 return "";
1044}
1045
1046void GLSLCodeGenerator::writeTypePrecision(const Type& type) {
1047 this->write(this->getTypePrecision(type));
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001048}
1049
ethannicholas5961bc92016-10-12 06:39:56 -07001050void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholas14efcbf2017-11-07 09:23:38 -05001051 if (!decl.fVars.size()) {
1052 return;
1053 }
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001054 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001055 for (const auto& stmt : decl.fVars) {
1056 VarDeclaration& var = (VarDeclaration&) *stmt;
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001057 if (wroteType) {
1058 this->write(", ");
1059 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001060 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001061 this->writeTypePrecision(decl.fBaseType);
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001062 this->writeType(decl.fBaseType);
1063 this->write(" ");
1064 wroteType = true;
1065 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001066 this->write(var.fVar->fName);
1067 for (const auto& size : var.fSizes) {
ethannicholasf789b382016-08-03 12:43:36 -07001068 this->write("[");
ethannicholas5961bc92016-10-12 06:39:56 -07001069 if (size) {
1070 this->writeExpression(*size, kTopLevel_Precedence);
1071 }
ethannicholasf789b382016-08-03 12:43:36 -07001072 this->write("]");
1073 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001074 if (var.fValue) {
ethannicholasf789b382016-08-03 12:43:36 -07001075 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001076 this->writeVarInitializer(*var.fVar, *var.fValue);
ethannicholasf789b382016-08-03 12:43:36 -07001077 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001078 if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001079 if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
Ethan Nicholas9e1138d2016-11-21 10:39:35 -05001080 fHeader.writeText("#extension ");
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001081 fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
Ethan Nicholas9e1138d2016-11-21 10:39:35 -05001082 fHeader.writeText(" : require\n");
Brian Salomon2a51de82016-11-16 12:06:01 -05001083 }
1084 fFoundImageDecl = true;
1085 }
Brian Osman4b2f9152018-04-17 11:19:57 -04001086 if (!fFoundExternalSamplerDecl && var.fVar->fType == *fContext.fSamplerExternalOES_Type) {
1087 if (fProgram.fSettings.fCaps->externalTextureExtensionString()) {
1088 fHeader.writeText("#extension ");
1089 fHeader.writeText(fProgram.fSettings.fCaps->externalTextureExtensionString());
Brian Osman6cd92682018-04-25 13:13:19 -04001090 fHeader.writeText(" : enable\n");
Brian Osman4b2f9152018-04-17 11:19:57 -04001091 }
Brian Osman061020e2018-04-17 14:22:15 -04001092 if (fProgram.fSettings.fCaps->secondExternalTextureExtensionString()) {
1093 fHeader.writeText("#extension ");
1094 fHeader.writeText(fProgram.fSettings.fCaps->secondExternalTextureExtensionString());
Brian Osman6cd92682018-04-25 13:13:19 -04001095 fHeader.writeText(" : enable\n");
Brian Osman061020e2018-04-17 14:22:15 -04001096 }
Brian Osman4b2f9152018-04-17 11:19:57 -04001097 fFoundExternalSamplerDecl = true;
1098 }
ethannicholasf789b382016-08-03 12:43:36 -07001099 }
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001100 if (wroteType) {
1101 this->write(";");
1102 }
ethannicholasf789b382016-08-03 12:43:36 -07001103}
1104
1105void GLSLCodeGenerator::writeStatement(const Statement& s) {
1106 switch (s.fKind) {
1107 case Statement::kBlock_Kind:
1108 this->writeBlock((Block&) s);
1109 break;
1110 case Statement::kExpression_Kind:
1111 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
1112 this->write(";");
1113 break;
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001114 case Statement::kReturn_Kind:
ethannicholasf789b382016-08-03 12:43:36 -07001115 this->writeReturnStatement((ReturnStatement&) s);
1116 break;
ethannicholas14fe8cc2016-09-07 13:37:16 -07001117 case Statement::kVarDeclarations_Kind:
ethannicholas5961bc92016-10-12 06:39:56 -07001118 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
ethannicholasf789b382016-08-03 12:43:36 -07001119 break;
1120 case Statement::kIf_Kind:
1121 this->writeIfStatement((IfStatement&) s);
1122 break;
1123 case Statement::kFor_Kind:
1124 this->writeForStatement((ForStatement&) s);
1125 break;
1126 case Statement::kWhile_Kind:
1127 this->writeWhileStatement((WhileStatement&) s);
1128 break;
1129 case Statement::kDo_Kind:
1130 this->writeDoStatement((DoStatement&) s);
1131 break;
Ethan Nicholasaf197692017-02-27 13:26:45 -05001132 case Statement::kSwitch_Kind:
1133 this->writeSwitchStatement((SwitchStatement&) s);
1134 break;
ethannicholasf789b382016-08-03 12:43:36 -07001135 case Statement::kBreak_Kind:
1136 this->write("break;");
1137 break;
1138 case Statement::kContinue_Kind:
1139 this->write("continue;");
1140 break;
1141 case Statement::kDiscard_Kind:
1142 this->write("discard;");
1143 break;
Ethan Nicholascb670962017-04-20 19:31:52 -04001144 case Statement::kNop_Kind:
1145 this->write(";");
1146 break;
ethannicholasf789b382016-08-03 12:43:36 -07001147 default:
1148 ABORT("unsupported statement: %s", s.description().c_str());
1149 }
1150}
1151
Ethan Nicholascb670962017-04-20 19:31:52 -04001152void GLSLCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
1153 for (const auto& s : statements) {
1154 if (!s->isEmpty()) {
1155 this->writeStatement(*s);
1156 this->writeLine();
1157 }
1158 }
1159}
1160
ethannicholasf789b382016-08-03 12:43:36 -07001161void GLSLCodeGenerator::writeBlock(const Block& b) {
1162 this->writeLine("{");
1163 fIndentation++;
Ethan Nicholascb670962017-04-20 19:31:52 -04001164 this->writeStatements(b.fStatements);
ethannicholasf789b382016-08-03 12:43:36 -07001165 fIndentation--;
1166 this->write("}");
1167}
1168
1169void GLSLCodeGenerator::writeIfStatement(const IfStatement& stmt) {
1170 this->write("if (");
1171 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
1172 this->write(") ");
1173 this->writeStatement(*stmt.fIfTrue);
1174 if (stmt.fIfFalse) {
1175 this->write(" else ");
1176 this->writeStatement(*stmt.fIfFalse);
1177 }
1178}
1179
1180void GLSLCodeGenerator::writeForStatement(const ForStatement& f) {
1181 this->write("for (");
Ethan Nicholasb310fd52017-06-09 13:46:34 -04001182 if (f.fInitializer && !f.fInitializer->isEmpty()) {
ethannicholasf789b382016-08-03 12:43:36 -07001183 this->writeStatement(*f.fInitializer);
1184 } else {
1185 this->write("; ");
1186 }
1187 if (f.fTest) {
1188 this->writeExpression(*f.fTest, kTopLevel_Precedence);
1189 }
1190 this->write("; ");
1191 if (f.fNext) {
1192 this->writeExpression(*f.fNext, kTopLevel_Precedence);
1193 }
1194 this->write(") ");
1195 this->writeStatement(*f.fStatement);
1196}
1197
1198void GLSLCodeGenerator::writeWhileStatement(const WhileStatement& w) {
1199 this->write("while (");
1200 this->writeExpression(*w.fTest, kTopLevel_Precedence);
1201 this->write(") ");
1202 this->writeStatement(*w.fStatement);
1203}
1204
1205void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
1206 this->write("do ");
1207 this->writeStatement(*d.fStatement);
1208 this->write(" while (");
1209 this->writeExpression(*d.fTest, kTopLevel_Precedence);
1210 this->write(");");
1211}
1212
Ethan Nicholasaf197692017-02-27 13:26:45 -05001213void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
1214 this->write("switch (");
1215 this->writeExpression(*s.fValue, kTopLevel_Precedence);
1216 this->writeLine(") {");
1217 fIndentation++;
1218 for (const auto& c : s.fCases) {
1219 if (c->fValue) {
1220 this->write("case ");
1221 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1222 this->writeLine(":");
1223 } else {
1224 this->writeLine("default:");
1225 }
1226 fIndentation++;
1227 for (const auto& stmt : c->fStatements) {
1228 this->writeStatement(*stmt);
1229 this->writeLine();
1230 }
1231 fIndentation--;
1232 }
1233 fIndentation--;
1234 this->write("}");
1235}
1236
ethannicholasf789b382016-08-03 12:43:36 -07001237void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1238 this->write("return");
1239 if (r.fExpression) {
1240 this->write(" ");
1241 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1242 }
1243 this->write(";");
1244}
1245
Ethan Nicholas762466e2017-06-29 10:03:38 -04001246void GLSLCodeGenerator::writeHeader() {
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001247 this->write(fProgram.fSettings.fCaps->versionDeclString());
ethannicholasf789b382016-08-03 12:43:36 -07001248 this->writeLine();
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001249 for (const auto& e : fProgram) {
1250 if (e.fKind == ProgramElement::kExtension_Kind) {
1251 this->writeExtension((Extension&) e);
ethannicholas5961bc92016-10-12 06:39:56 -07001252 }
1253 }
Brian Osmancd3261a2018-01-16 13:52:29 +00001254 if (!fProgram.fSettings.fCaps->canUseFragCoord()) {
1255 Layout layout;
1256 switch (fProgram.fKind) {
1257 case Program::kVertex_Kind: {
1258 Modifiers modifiers(layout, Modifiers::kOut_Flag | Modifiers::kHighp_Flag);
1259 this->writeModifiers(modifiers, true);
1260 this->write("vec4 sk_FragCoord_Workaround;\n");
1261 break;
1262 }
1263 case Program::kFragment_Kind: {
1264 Modifiers modifiers(layout, Modifiers::kIn_Flag | Modifiers::kHighp_Flag);
1265 this->writeModifiers(modifiers, true);
1266 this->write("vec4 sk_FragCoord_Workaround;\n");
1267 break;
1268 }
1269 default:
1270 break;
1271 }
1272 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001273}
1274
Ethan Nicholas762466e2017-06-29 10:03:38 -04001275void GLSLCodeGenerator::writeProgramElement(const ProgramElement& e) {
1276 switch (e.fKind) {
1277 case ProgramElement::kExtension_Kind:
1278 break;
1279 case ProgramElement::kVar_Kind: {
1280 VarDeclarations& decl = (VarDeclarations&) e;
1281 if (decl.fVars.size() > 0) {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001282 int builtin = ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
Ethan Nicholas762466e2017-06-29 10:03:38 -04001283 if (builtin == -1) {
1284 // normal var
1285 this->writeVarDeclarations(decl, true);
1286 this->writeLine();
1287 } else if (builtin == SK_FRAGCOLOR_BUILTIN &&
1288 fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput()) {
Brian Salomondc092132018-04-04 10:14:16 -04001289 if (fProgram.fSettings.fFragColorIsInOut) {
1290 this->write("inout ");
1291 } else {
1292 this->write("out ");
1293 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001294 if (usesPrecisionModifiers()) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001295 this->write("mediump ");
Mike Klein5ce39722017-06-27 22:52:03 +00001296 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001297 this->writeLine("vec4 sk_FragColor;");
Mike Klein5ce39722017-06-27 22:52:03 +00001298 }
Mike Klein5ce39722017-06-27 22:52:03 +00001299 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001300 break;
Mike Klein5ce39722017-06-27 22:52:03 +00001301 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001302 case ProgramElement::kInterfaceBlock_Kind:
1303 this->writeInterfaceBlock((InterfaceBlock&) e);
1304 break;
1305 case ProgramElement::kFunction_Kind:
1306 this->writeFunction((FunctionDefinition&) e);
1307 break;
Chris Daltonf1b47bb2017-10-06 11:57:51 -06001308 case ProgramElement::kModifiers_Kind: {
1309 const Modifiers& modifiers = ((ModifiersDeclaration&) e).fModifiers;
1310 if (!fFoundGSInvocations && modifiers.fLayout.fInvocations >= 0) {
1311 if (fProgram.fSettings.fCaps->gsInvocationsExtensionString()) {
1312 fHeader.writeText("#extension ");
1313 fHeader.writeText(fProgram.fSettings.fCaps->gsInvocationsExtensionString());
1314 fHeader.writeText(" : require\n");
1315 }
1316 fFoundGSInvocations = true;
1317 }
1318 this->writeModifiers(modifiers, true);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001319 this->writeLine(";");
1320 break;
Chris Daltonf1b47bb2017-10-06 11:57:51 -06001321 }
Ethan Nicholasaae47c82017-11-10 15:34:03 -05001322 case ProgramElement::kEnum_Kind:
1323 break;
Ethan Nicholas762466e2017-06-29 10:03:38 -04001324 default:
1325 printf("%s\n", e.description().c_str());
1326 ABORT("unsupported program element");
Ethan Nicholasc0709392017-06-27 11:20:22 -04001327 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001328}
1329
1330bool GLSLCodeGenerator::generateCode() {
1331 OutputStream* rawOut = fOut;
1332 fOut = &fHeader;
1333 fProgramKind = fProgram.fKind;
1334 this->writeHeader();
Chris Dalton8fd79552018-01-11 00:46:14 -05001335 if (Program::kGeometry_Kind == fProgramKind &&
1336 fProgram.fSettings.fCaps->geometryShaderExtensionString()) {
1337 fHeader.writeText("#extension ");
1338 fHeader.writeText(fProgram.fSettings.fCaps->geometryShaderExtensionString());
1339 fHeader.writeText(" : require\n");
1340 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001341 StringStream body;
1342 fOut = &body;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001343 for (const auto& e : fProgram) {
1344 this->writeProgramElement(e);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001345 }
1346 fOut = rawOut;
ethannicholasddb37d62016-10-20 09:54:00 -07001347
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001348 write_stringstream(fHeader, *rawOut);
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001349 if (this->usesPrecisionModifiers()) {
1350 this->writeLine("precision mediump float;");
1351 }
Ethan Nicholas6e6525c2018-01-03 17:03:56 -05001352 write_stringstream(fExtraFunctions, *rawOut);
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001353 write_stringstream(body, *rawOut);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001354 return true;
ethannicholasf789b382016-08-03 12:43:36 -07001355}
1356
1357}