blob: 127145a19213118132d822bf0d78a119c911e70c [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/sksl/SkSLGLSLCodeGenerator.h"
ethannicholasf789b382016-08-03 12:43:36 -07009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/sksl/SkSLCompiler.h"
11#include "src/sksl/ir/SkSLExpressionStatement.h"
12#include "src/sksl/ir/SkSLExtension.h"
13#include "src/sksl/ir/SkSLIndexExpression.h"
14#include "src/sksl/ir/SkSLModifiersDeclaration.h"
15#include "src/sksl/ir/SkSLNop.h"
16#include "src/sksl/ir/SkSLVariableReference.h"
ethannicholasf789b382016-08-03 12:43:36 -070017
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -040018#ifndef SKSL_STANDALONE
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "include/private/SkOnce.h"
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -040020#endif
21
ethannicholasf789b382016-08-03 12:43:36 -070022namespace SkSL {
23
24void GLSLCodeGenerator::write(const char* s) {
25 if (s[0] == 0) {
26 return;
27 }
28 if (fAtLineStart) {
29 for (int i = 0; i < fIndentation; i++) {
Ethan Nicholas9e1138d2016-11-21 10:39:35 -050030 fOut->writeText(" ");
ethannicholasf789b382016-08-03 12:43:36 -070031 }
32 }
Ethan Nicholas9e1138d2016-11-21 10:39:35 -050033 fOut->writeText(s);
ethannicholasf789b382016-08-03 12:43:36 -070034 fAtLineStart = false;
35}
36
37void GLSLCodeGenerator::writeLine(const char* s) {
38 this->write(s);
Ethan Nicholas762466e2017-06-29 10:03:38 -040039 fOut->writeText(fLineEnding);
ethannicholasf789b382016-08-03 12:43:36 -070040 fAtLineStart = true;
41}
42
Ethan Nicholas0df1b042017-03-31 13:56:23 -040043void GLSLCodeGenerator::write(const String& s) {
ethannicholasf789b382016-08-03 12:43:36 -070044 this->write(s.c_str());
45}
46
Ethan Nicholas5b5f0962017-09-11 13:50:14 -070047void GLSLCodeGenerator::write(StringFragment s) {
48 if (!s.fLength) {
49 return;
50 }
51 if (fAtLineStart) {
52 for (int i = 0; i < fIndentation; i++) {
53 fOut->writeText(" ");
54 }
55 }
56 fOut->write(s.fChars, s.fLength);
57 fAtLineStart = false;
58}
59
Ethan Nicholas0df1b042017-03-31 13:56:23 -040060void GLSLCodeGenerator::writeLine(const String& s) {
ethannicholasf789b382016-08-03 12:43:36 -070061 this->writeLine(s.c_str());
62}
63
64void GLSLCodeGenerator::writeLine() {
65 this->writeLine("");
66}
67
Ethan Nicholas88f6d372018-07-27 10:03:46 -040068void GLSLCodeGenerator::writeExtension(const String& name) {
69 this->writeExtension(name, true);
70}
71
72void GLSLCodeGenerator::writeExtension(const String& name, bool require) {
73 fExtensions.writeText("#extension ");
74 fExtensions.write(name.c_str(), name.length());
75 fExtensions.writeText(require ? " : require\n" : " : enable\n");
ethannicholasf789b382016-08-03 12:43:36 -070076}
77
Ethan Nicholasf7b88202017-09-18 14:10:39 -040078bool GLSLCodeGenerator::usesPrecisionModifiers() const {
79 return fProgram.fSettings.fCaps->usesPrecisionModifiers();
80}
81
82String GLSLCodeGenerator::getTypeName(const Type& type) {
83 switch (type.kind()) {
84 case Type::kVector_Kind: {
85 Type component = type.componentType();
86 String result;
87 if (component == *fContext.fFloat_Type || component == *fContext.fHalf_Type) {
88 result = "vec";
89 }
Ethan Nicholase1f55022019-02-05 17:17:40 -050090 else if (component.isSigned()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -040091 result = "ivec";
92 }
Ethan Nicholase1f55022019-02-05 17:17:40 -050093 else if (component.isUnsigned()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -040094 result = "uvec";
95 }
96 else if (component == *fContext.fBool_Type) {
97 result = "bvec";
98 }
99 else {
100 ABORT("unsupported vector type");
101 }
102 result += to_string(type.columns());
103 return result;
104 }
105 case Type::kMatrix_Kind: {
106 String result;
107 Type component = type.componentType();
108 if (component == *fContext.fFloat_Type || component == *fContext.fHalf_Type) {
109 result = "mat";
110 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400111 else {
112 ABORT("unsupported matrix type");
113 }
114 result += to_string(type.columns());
115 if (type.columns() != type.rows()) {
116 result += "x";
117 result += to_string(type.rows());
118 }
119 return result;
120 }
121 case Type::kArray_Kind: {
122 String result = this->getTypeName(type.componentType()) + "[";
123 if (type.columns() != -1) {
124 result += to_string(type.columns());
125 }
126 result += "]";
127 return result;
128 }
129 case Type::kScalar_Kind: {
130 if (type == *fContext.fHalf_Type) {
131 return "float";
132 }
133 else if (type == *fContext.fShort_Type) {
134 return "int";
135 }
136 else if (type == *fContext.fUShort_Type) {
137 return "uint";
138 }
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400139 else if (type == *fContext.fByte_Type) {
140 return "int";
141 }
142 else if (type == *fContext.fUByte_Type) {
143 return "uint";
144 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400145 else {
146 return type.name();
147 }
148 break;
149 }
Ethan Nicholasdb80f692019-11-22 14:06:12 -0500150 case Type::kEnum_Kind:
151 return "int";
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400152 default:
153 return type.name();
154 }
155}
156
ethannicholasf789b382016-08-03 12:43:36 -0700157void GLSLCodeGenerator::writeType(const Type& type) {
158 if (type.kind() == Type::kStruct_Kind) {
159 for (const Type* search : fWrittenStructs) {
160 if (*search == type) {
161 // already written
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700162 this->write(type.fName);
ethannicholasf789b382016-08-03 12:43:36 -0700163 return;
164 }
165 }
166 fWrittenStructs.push_back(&type);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700167 this->write("struct ");
168 this->write(type.fName);
169 this->writeLine(" {");
ethannicholasf789b382016-08-03 12:43:36 -0700170 fIndentation++;
171 for (const auto& f : type.fields()) {
ethannicholas5961bc92016-10-12 06:39:56 -0700172 this->writeModifiers(f.fModifiers, false);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -0400173 this->writeTypePrecision(*f.fType);
ethannicholasf789b382016-08-03 12:43:36 -0700174 // sizes (which must be static in structs) are part of the type name here
ethannicholas0730be72016-09-01 07:59:02 -0700175 this->writeType(*f.fType);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700176 this->write(" ");
177 this->write(f.fName);
178 this->writeLine(";");
ethannicholasf789b382016-08-03 12:43:36 -0700179 }
180 fIndentation--;
Ethan Nicholas19671772016-11-28 16:30:17 -0500181 this->write("}");
ethannicholasf789b382016-08-03 12:43:36 -0700182 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400183 this->write(this->getTypeName(type));
ethannicholasf789b382016-08-03 12:43:36 -0700184 }
185}
186
187void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
188 switch (expr.fKind) {
189 case Expression::kBinary_Kind:
190 this->writeBinaryExpression((BinaryExpression&) expr, parentPrecedence);
191 break;
192 case Expression::kBoolLiteral_Kind:
193 this->writeBoolLiteral((BoolLiteral&) expr);
194 break;
195 case Expression::kConstructor_Kind:
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400196 this->writeConstructor((Constructor&) expr, parentPrecedence);
ethannicholasf789b382016-08-03 12:43:36 -0700197 break;
198 case Expression::kIntLiteral_Kind:
199 this->writeIntLiteral((IntLiteral&) expr);
200 break;
201 case Expression::kFieldAccess_Kind:
202 this->writeFieldAccess(((FieldAccess&) expr));
203 break;
204 case Expression::kFloatLiteral_Kind:
205 this->writeFloatLiteral(((FloatLiteral&) expr));
206 break;
207 case Expression::kFunctionCall_Kind:
208 this->writeFunctionCall((FunctionCall&) expr);
209 break;
210 case Expression::kPrefix_Kind:
211 this->writePrefixExpression((PrefixExpression&) expr, parentPrecedence);
212 break;
213 case Expression::kPostfix_Kind:
214 this->writePostfixExpression((PostfixExpression&) expr, parentPrecedence);
215 break;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400216 case Expression::kSetting_Kind:
217 this->writeSetting((Setting&) expr);
218 break;
ethannicholasf789b382016-08-03 12:43:36 -0700219 case Expression::kSwizzle_Kind:
220 this->writeSwizzle((Swizzle&) expr);
221 break;
222 case Expression::kVariableReference_Kind:
223 this->writeVariableReference((VariableReference&) expr);
224 break;
225 case Expression::kTernary_Kind:
226 this->writeTernaryExpression((TernaryExpression&) expr, parentPrecedence);
227 break;
228 case Expression::kIndex_Kind:
229 this->writeIndexExpression((IndexExpression&) expr);
230 break;
231 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500232#ifdef SK_DEBUG
ethannicholasf789b382016-08-03 12:43:36 -0700233 ABORT("unsupported expression: %s", expr.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500234#endif
235 break;
ethannicholasf789b382016-08-03 12:43:36 -0700236 }
237}
238
ethannicholas5961bc92016-10-12 06:39:56 -0700239static bool is_abs(Expression& expr) {
240 if (expr.fKind != Expression::kFunctionCall_Kind) {
241 return false;
242 }
243 return ((FunctionCall&) expr).fFunction.fName == "abs";
244}
245
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500246// turns min(abs(x), y) into ((tmpVar1 = abs(x)) < (tmpVar2 = y) ? tmpVar1 : tmpVar2) to avoid a
ethannicholas5961bc92016-10-12 06:39:56 -0700247// Tegra3 compiler bug.
248void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400249 SkASSERT(!fProgram.fSettings.fCaps->canUseMinAndAbsTogether());
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400250 String tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
251 String tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400252 this->fFunctionHeader += String(" ") + this->getTypePrecision(absExpr.fType) +
253 this->getTypeName(absExpr.fType) + " " + tmpVar1 + ";\n";
254 this->fFunctionHeader += String(" ") + this->getTypePrecision(otherExpr.fType) +
255 this->getTypeName(otherExpr.fType) + " " + tmpVar2 + ";\n";
ethannicholas5961bc92016-10-12 06:39:56 -0700256 this->write("((" + tmpVar1 + " = ");
257 this->writeExpression(absExpr, kTopLevel_Precedence);
258 this->write(") < (" + tmpVar2 + " = ");
259 this->writeExpression(otherExpr, kAssignment_Precedence);
260 this->write(") ? " + tmpVar1 + " : " + tmpVar2 + ")");
261}
262
Ethan Nicholas6e6525c2018-01-03 17:03:56 -0500263void GLSLCodeGenerator::writeInverseSqrtHack(const Expression& x) {
264 this->write("(1.0 / sqrt(");
265 this->writeExpression(x, kTopLevel_Precedence);
266 this->write("))");
267}
268
269void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) {
270 String name;
271 if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
272 name = "_determinant2";
273 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
274 fWrittenIntrinsics.insert(name);
275 fExtraFunctions.writeText((
276 "float " + name + "(mat2 m) {"
277 " return m[0][0] * m[1][1] - m[0][1] * m[1][0];"
278 "}"
279 ).c_str());
280 }
281 }
282 else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
283 name = "_determinant3";
284 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
285 fWrittenIntrinsics.insert(name);
286 fExtraFunctions.writeText((
287 "float " + name + "(mat3 m) {"
288 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];"
289 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];"
290 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];"
291 " float b01 = a22 * a11 - a12 * a21;"
292 " float b11 = -a22 * a10 + a12 * a20;"
293 " float b21 = a21 * a10 - a11 * a20;"
294 " return a00 * b01 + a01 * b11 + a02 * b21;"
295 "}"
296 ).c_str());
297 }
298 }
299 else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
300 name = "_determinant3";
301 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
302 fWrittenIntrinsics.insert(name);
303 fExtraFunctions.writeText((
304 "mat4 " + name + "(mat4 m) {"
305 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];"
306 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];"
307 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];"
308 " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];"
309 " float b00 = a00 * a11 - a01 * a10;"
310 " float b01 = a00 * a12 - a02 * a10;"
311 " float b02 = a00 * a13 - a03 * a10;"
312 " float b03 = a01 * a12 - a02 * a11;"
313 " float b04 = a01 * a13 - a03 * a11;"
314 " float b05 = a02 * a13 - a03 * a12;"
315 " float b06 = a20 * a31 - a21 * a30;"
316 " float b07 = a20 * a32 - a22 * a30;"
317 " float b08 = a20 * a33 - a23 * a30;"
318 " float b09 = a21 * a32 - a22 * a31;"
319 " float b10 = a21 * a33 - a23 * a31;"
320 " float b11 = a22 * a33 - a23 * a32;"
321 " return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;"
322 "}"
323 ).c_str());
324 }
325 }
326 else {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400327 SkASSERT(false);
Ethan Nicholas6e6525c2018-01-03 17:03:56 -0500328 }
329 this->write(name + "(");
330 this->writeExpression(mat, kTopLevel_Precedence);
331 this->write(")");
332}
333
334void GLSLCodeGenerator::writeInverseHack(const Expression& mat) {
335 String name;
336 if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
337 name = "_inverse2";
338 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
339 fWrittenIntrinsics.insert(name);
340 fExtraFunctions.writeText((
341 "mat2 " + name + "(mat2 m) {"
342 " return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / "
343 "(m[0][0] * m[1][1] - m[0][1] * m[1][0]);"
344 "}"
345 ).c_str());
346 }
347 }
348 else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
349 name = "_inverse3";
350 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
351 fWrittenIntrinsics.insert(name);
352 fExtraFunctions.writeText((
353 "mat3 " + name + "(mat3 m) {"
354 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];"
355 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];"
356 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];"
357 " float b01 = a22 * a11 - a12 * a21;"
358 " float b11 = -a22 * a10 + a12 * a20;"
359 " float b21 = a21 * a10 - a11 * a20;"
360 " float det = a00 * b01 + a01 * b11 + a02 * b21;"
361 " return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),"
362 " b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),"
363 " b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;"
364 "}"
365 ).c_str());
366 }
367 }
368 else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
369 name = "_inverse4";
370 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
371 fWrittenIntrinsics.insert(name);
372 fExtraFunctions.writeText((
373 "mat4 " + name + "(mat4 m) {"
374 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];"
375 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];"
376 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];"
377 " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];"
378 " float b00 = a00 * a11 - a01 * a10;"
379 " float b01 = a00 * a12 - a02 * a10;"
380 " float b02 = a00 * a13 - a03 * a10;"
381 " float b03 = a01 * a12 - a02 * a11;"
382 " float b04 = a01 * a13 - a03 * a11;"
383 " float b05 = a02 * a13 - a03 * a12;"
384 " float b06 = a20 * a31 - a21 * a30;"
385 " float b07 = a20 * a32 - a22 * a30;"
386 " float b08 = a20 * a33 - a23 * a30;"
387 " float b09 = a21 * a32 - a22 * a31;"
388 " float b10 = a21 * a33 - a23 * a31;"
389 " float b11 = a22 * a33 - a23 * a32;"
390 " float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - "
391 " b04 * b07 + b05 * b06;"
392 " return mat4("
393 " a11 * b11 - a12 * b10 + a13 * b09,"
394 " a02 * b10 - a01 * b11 - a03 * b09,"
395 " a31 * b05 - a32 * b04 + a33 * b03,"
396 " a22 * b04 - a21 * b05 - a23 * b03,"
397 " a12 * b08 - a10 * b11 - a13 * b07,"
398 " a00 * b11 - a02 * b08 + a03 * b07,"
399 " a32 * b02 - a30 * b05 - a33 * b01,"
400 " a20 * b05 - a22 * b02 + a23 * b01,"
401 " a10 * b10 - a11 * b08 + a13 * b06,"
402 " a01 * b08 - a00 * b10 - a03 * b06,"
403 " a30 * b04 - a31 * b02 + a33 * b00,"
404 " a21 * b02 - a20 * b04 - a23 * b00,"
405 " a11 * b07 - a10 * b09 - a12 * b06,"
406 " a00 * b09 - a01 * b07 + a02 * b06,"
407 " a31 * b01 - a30 * b03 - a32 * b00,"
408 " a20 * b03 - a21 * b01 + a22 * b00) / det;"
409 "}"
410 ).c_str());
411 }
412 }
413 else {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400414 SkASSERT(false);
Ethan Nicholas6e6525c2018-01-03 17:03:56 -0500415 }
416 this->write(name + "(");
417 this->writeExpression(mat, kTopLevel_Precedence);
418 this->write(")");
419}
420
421void GLSLCodeGenerator::writeTransposeHack(const Expression& mat) {
422 String name = "transpose" + to_string(mat.fType.columns()) + to_string(mat.fType.rows());
423 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
424 fWrittenIntrinsics.insert(name);
425 String type = this->getTypeName(mat.fType);
426 const Type& base = mat.fType.componentType();
427 String transposed = this->getTypeName(base.toCompound(fContext,
428 mat.fType.rows(),
429 mat.fType.columns()));
430 fExtraFunctions.writeText((transposed + " " + name + "(" + type + " m) {\nreturn " +
431 transposed + "(").c_str());
432 const char* separator = "";
433 for (int row = 0; row < mat.fType.rows(); ++row) {
434 for (int column = 0; column < mat.fType.columns(); ++column) {
435 fExtraFunctions.writeText(separator);
436 fExtraFunctions.writeText(("m[" + to_string(column) + "][" + to_string(row) +
437 "]").c_str());
438 separator = ", ";
439 }
440 }
441 fExtraFunctions.writeText("); }");
442 }
443 this->write(name + "(");
444 this->writeExpression(mat, kTopLevel_Precedence);
445 this->write(")");
446}
447
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400448std::unordered_map<StringFragment, GLSLCodeGenerator::FunctionClass>*
449 GLSLCodeGenerator::fFunctionClasses = nullptr;
450
ethannicholasf789b382016-08-03 12:43:36 -0700451void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400452#ifdef SKSL_STANDALONE
453 if (!fFunctionClasses) {
454#else
455 static SkOnce once;
456 once([] {
457#endif
458 fFunctionClasses = new std::unordered_map<StringFragment, FunctionClass>();
Adrienne Walker92b161f2018-08-22 10:41:52 -0700459 (*fFunctionClasses)["abs"] = FunctionClass::kAbs;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400460 (*fFunctionClasses)["atan"] = FunctionClass::kAtan;
461 (*fFunctionClasses)["determinant"] = FunctionClass::kDeterminant;
Chris Daltonb8af5ad2019-02-25 14:54:21 -0700462 (*fFunctionClasses)["dFdx"] = FunctionClass::kDFdx;
463 (*fFunctionClasses)["dFdy"] = FunctionClass::kDFdy;
464 (*fFunctionClasses)["fwidth"] = FunctionClass::kFwidth;
Chris Daltona7086182018-11-16 09:33:43 -0500465 (*fFunctionClasses)["fma"] = FunctionClass::kFMA;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400466 (*fFunctionClasses)["fract"] = FunctionClass::kFract;
467 (*fFunctionClasses)["inverse"] = FunctionClass::kInverse;
468 (*fFunctionClasses)["inverseSqrt"] = FunctionClass::kInverseSqrt;
469 (*fFunctionClasses)["min"] = FunctionClass::kMin;
Adrienne Walker2f4c09b2018-08-22 16:04:57 -0700470 (*fFunctionClasses)["pow"] = FunctionClass::kPow;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400471 (*fFunctionClasses)["saturate"] = FunctionClass::kSaturate;
Ethan Nicholas13863662019-07-29 13:05:15 -0400472 (*fFunctionClasses)["sample"] = FunctionClass::kTexture;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400473 (*fFunctionClasses)["transpose"] = FunctionClass::kTranspose;
ethannicholas5961bc92016-10-12 06:39:56 -0700474 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400475#ifndef SKSL_STANDALONE
476 );
477#endif
478 const auto found = c.fFunction.fBuiltin ? fFunctionClasses->find(c.fFunction.fName) :
479 fFunctionClasses->end();
Brian Osman8a83ca42018-02-12 14:32:17 -0500480 bool isTextureFunctionWithBias = false;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400481 bool nameWritten = false;
482 if (found != fFunctionClasses->end()) {
483 switch (found->second) {
Adrienne Walker92b161f2018-08-22 10:41:52 -0700484 case FunctionClass::kAbs: {
485 if (!fProgram.fSettings.fCaps->emulateAbsIntFunction())
486 break;
487 SkASSERT(c.fArguments.size() == 1);
488 if (c.fArguments[0]->fType != *fContext.fInt_Type)
489 break;
490 // abs(int) on Intel OSX is incorrect, so emulate it:
491 String name = "_absemulation";
492 this->write(name);
493 nameWritten = true;
494 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
495 fWrittenIntrinsics.insert(name);
496 fExtraFunctions.writeText((
497 "int " + name + "(int x) {\n"
498 " return x * sign(x);\n"
499 "}\n"
500 ).c_str());
501 }
502 break;
503 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400504 case FunctionClass::kAtan:
505 if (fProgram.fSettings.fCaps->mustForceNegatedAtanParamToFloat() &&
506 c.fArguments.size() == 2 &&
507 c.fArguments[1]->fKind == Expression::kPrefix_Kind) {
508 const PrefixExpression& p = (PrefixExpression&) *c.fArguments[1];
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400509 if (p.fOperator == Token::Kind::TK_MINUS) {
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400510 this->write("atan(");
511 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
512 this->write(", -1.0 * ");
513 this->writeExpression(*p.fOperand, kMultiplicative_Precedence);
514 this->write(")");
515 return;
516 }
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500517 }
518 break;
Chris Daltonb8af5ad2019-02-25 14:54:21 -0700519 case FunctionClass::kDFdy:
520 if (fProgram.fSettings.fFlipY) {
521 // Flipping Y also negates the Y derivatives.
522 this->write("-dFdy");
523 nameWritten = true;
524 }
John Stiles30212b72020-06-11 17:55:07 -0400525 [[fallthrough]];
Chris Daltonb8af5ad2019-02-25 14:54:21 -0700526 case FunctionClass::kDFdx:
527 case FunctionClass::kFwidth:
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400528 if (!fFoundDerivatives &&
529 fProgram.fSettings.fCaps->shaderDerivativeExtensionString()) {
530 SkASSERT(fProgram.fSettings.fCaps->shaderDerivativeSupport());
531 this->writeExtension(fProgram.fSettings.fCaps->shaderDerivativeExtensionString());
532 fFoundDerivatives = true;
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500533 }
534 break;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400535 case FunctionClass::kDeterminant:
536 if (fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
537 SkASSERT(c.fArguments.size() == 1);
538 this->writeDeterminantHack(*c.fArguments[0]);
539 return;
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500540 }
541 break;
Chris Daltona7086182018-11-16 09:33:43 -0500542 case FunctionClass::kFMA:
543 if (!fProgram.fSettings.fCaps->builtinFMASupport()) {
544 SkASSERT(c.fArguments.size() == 3);
545 this->write("((");
546 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
547 this->write(") * (");
548 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
549 this->write(") + (");
550 this->writeExpression(*c.fArguments[2], kSequence_Precedence);
551 this->write("))");
552 return;
553 }
554 break;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400555 case FunctionClass::kFract:
556 if (!fProgram.fSettings.fCaps->canUseFractForNegativeValues()) {
557 SkASSERT(c.fArguments.size() == 1);
558 this->write("(0.5 - sign(");
559 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
560 this->write(") * (0.5 - fract(abs(");
561 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
562 this->write("))))");
563 return;
564 }
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500565 break;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400566 case FunctionClass::kInverse:
567 if (fProgram.fSettings.fCaps->generation() < k140_GrGLSLGeneration) {
568 SkASSERT(c.fArguments.size() == 1);
569 this->writeInverseHack(*c.fArguments[0]);
570 return;
571 }
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500572 break;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400573 case FunctionClass::kInverseSqrt:
574 if (fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
575 SkASSERT(c.fArguments.size() == 1);
576 this->writeInverseSqrtHack(*c.fArguments[0]);
577 return;
578 }
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500579 break;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400580 case FunctionClass::kMin:
581 if (!fProgram.fSettings.fCaps->canUseMinAndAbsTogether()) {
582 SkASSERT(c.fArguments.size() == 2);
583 if (is_abs(*c.fArguments[0])) {
584 this->writeMinAbsHack(*c.fArguments[0], *c.fArguments[1]);
585 return;
586 }
587 if (is_abs(*c.fArguments[1])) {
588 // note that this violates the GLSL left-to-right evaluation semantics.
589 // I doubt it will ever end up mattering, but it's worth calling out.
590 this->writeMinAbsHack(*c.fArguments[1], *c.fArguments[0]);
591 return;
592 }
593 }
594 break;
Adrienne Walker2f4c09b2018-08-22 16:04:57 -0700595 case FunctionClass::kPow:
596 if (!fProgram.fSettings.fCaps->removePowWithConstantExponent()) {
597 break;
598 }
599 // pow(x, y) on some NVIDIA drivers causes crashes if y is a
600 // constant. It's hard to tell what constitutes "constant" here
601 // so just replace in all cases.
602
603 // Change pow(x, y) into exp2(y * log2(x))
604 this->write("exp2(");
605 this->writeExpression(*c.fArguments[1], kMultiplicative_Precedence);
606 this->write(" * log2(");
607 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
608 this->write("))");
609 return;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400610 case FunctionClass::kSaturate:
611 SkASSERT(c.fArguments.size() == 1);
612 this->write("clamp(");
613 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
614 this->write(", 0.0, 1.0)");
615 return;
616 case FunctionClass::kTexture: {
617 const char* dim = "";
618 bool proj = false;
619 switch (c.fArguments[0]->fType.dimensions()) {
620 case SpvDim1D:
621 dim = "1D";
622 isTextureFunctionWithBias = true;
623 if (c.fArguments[1]->fType == *fContext.fFloat_Type) {
624 proj = false;
625 } else {
626 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
627 proj = true;
628 }
629 break;
630 case SpvDim2D:
631 dim = "2D";
632 if (c.fArguments[0]->fType != *fContext.fSamplerExternalOES_Type) {
633 isTextureFunctionWithBias = true;
634 }
635 if (c.fArguments[1]->fType == *fContext.fFloat2_Type) {
636 proj = false;
637 } else {
638 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat3_Type);
639 proj = true;
640 }
641 break;
642 case SpvDim3D:
643 dim = "3D";
644 isTextureFunctionWithBias = true;
645 if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
646 proj = false;
647 } else {
648 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat4_Type);
649 proj = true;
650 }
651 break;
652 case SpvDimCube:
653 dim = "Cube";
654 isTextureFunctionWithBias = true;
655 proj = false;
656 break;
657 case SpvDimRect:
Khushal Sagar2cb13152019-09-11 23:17:26 +0000658 dim = "2DRect";
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400659 proj = false;
660 break;
661 case SpvDimBuffer:
662 SkASSERT(false); // doesn't exist
663 dim = "Buffer";
664 proj = false;
665 break;
666 case SpvDimSubpassData:
667 SkASSERT(false); // doesn't exist
668 dim = "SubpassData";
669 proj = false;
670 break;
671 }
Ethan Nicholas13863662019-07-29 13:05:15 -0400672 if (fTextureFunctionOverride != "") {
673 this->write(fTextureFunctionOverride.c_str());
674 } else {
675 this->write("texture");
676 if (fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
677 this->write(dim);
678 }
679 if (proj) {
680 this->write("Proj");
681 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400682 }
683 nameWritten = true;
684 break;
685 }
686 case FunctionClass::kTranspose:
687 if (fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
688 SkASSERT(c.fArguments.size() == 1);
689 this->writeTransposeHack(*c.fArguments[0]);
690 return;
691 }
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500692 break;
693 }
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400694 }
695 if (!nameWritten) {
Ethan Nicholas2b3dab62016-11-28 12:03:26 -0500696 this->write(c.fFunction.fName);
697 }
698 this->write("(");
ethannicholasf789b382016-08-03 12:43:36 -0700699 const char* separator = "";
700 for (const auto& arg : c.fArguments) {
701 this->write(separator);
702 separator = ", ";
703 this->writeExpression(*arg, kSequence_Precedence);
704 }
Brian Osman8a83ca42018-02-12 14:32:17 -0500705 if (fProgram.fSettings.fSharpenTextures && isTextureFunctionWithBias) {
706 this->write(", -0.5");
707 }
ethannicholasf789b382016-08-03 12:43:36 -0700708 this->write(")");
709}
710
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400711void GLSLCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
712 if (c.fArguments.size() == 1 &&
Ethan Nicholase1f55022019-02-05 17:17:40 -0500713 (this->getTypeName(c.fType) == this->getTypeName(c.fArguments[0]->fType) ||
714 (c.fType.kind() == Type::kScalar_Kind &&
715 c.fArguments[0]->fType == *fContext.fFloatLiteral_Type))) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400716 // in cases like half(float), they're different types as far as SkSL is concerned but the
717 // same type as far as GLSL is concerned. We avoid a redundant float(float) by just writing
718 // out the inner expression here.
719 this->writeExpression(*c.fArguments[0], parentPrecedence);
720 return;
721 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400722 this->writeType(c.fType);
723 this->write("(");
ethannicholasf789b382016-08-03 12:43:36 -0700724 const char* separator = "";
725 for (const auto& arg : c.fArguments) {
726 this->write(separator);
727 separator = ", ";
728 this->writeExpression(*arg, kSequence_Precedence);
729 }
730 this->write(")");
731}
732
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500733void GLSLCodeGenerator::writeFragCoord() {
Brian Osmancd3261a2018-01-16 13:52:29 +0000734 if (!fProgram.fSettings.fCaps->canUseFragCoord()) {
Brian Salomondba65f92018-01-22 08:43:38 -0500735 if (!fSetupFragCoordWorkaround) {
736 const char* precision = usesPrecisionModifiers() ? "highp " : "";
737 fFunctionHeader += precision;
738 fFunctionHeader += " float sk_FragCoord_InvW = 1. / sk_FragCoord_Workaround.w;\n";
739 fFunctionHeader += precision;
740 fFunctionHeader += " vec4 sk_FragCoord_Resolved = "
741 "vec4(sk_FragCoord_Workaround.xyz * sk_FragCoord_InvW, sk_FragCoord_InvW);\n";
742 // Ensure that we get exact .5 values for x and y.
743 fFunctionHeader += " sk_FragCoord_Resolved.xy = floor(sk_FragCoord_Resolved.xy) + "
744 "vec2(.5);\n";
745 fSetupFragCoordWorkaround = true;
746 }
747 this->write("sk_FragCoord_Resolved");
Brian Osmancd3261a2018-01-16 13:52:29 +0000748 return;
749 }
750
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500751 // We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
752 // to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
753 // declaration varies in earlier GLSL specs. So it is simpler to omit it.
754 if (!fProgram.fSettings.fFlipY) {
755 this->write("gl_FragCoord");
756 } else if (const char* extension =
Ethan Nicholascd700e92018-08-24 16:43:57 -0400757 fProgram.fSettings.fCaps->fragCoordConventionsExtensionString()) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500758 if (!fSetupFragPositionGlobal) {
759 if (fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
Ethan Nicholas88f6d372018-07-27 10:03:46 -0400760 this->writeExtension(extension);
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500761 }
Ethan Nicholas88f6d372018-07-27 10:03:46 -0400762 fGlobals.writeText("layout(origin_upper_left) in vec4 gl_FragCoord;\n");
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500763 fSetupFragPositionGlobal = true;
Greg Daniele8e4a3e2016-12-12 17:20:42 +0000764 }
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500765 this->write("gl_FragCoord");
Greg Daniele8e4a3e2016-12-12 17:20:42 +0000766 } else {
Ethan Nicholascd700e92018-08-24 16:43:57 -0400767 if (!fSetupFragPositionLocal) {
Michael Ludwig5e1f6ea2018-12-03 15:14:50 -0500768 fFunctionHeader += usesPrecisionModifiers() ? "highp " : "";
Michael Ludwigf0b60442018-12-10 14:43:38 +0000769 fFunctionHeader += " vec4 sk_FragCoord = vec4(gl_FragCoord.x, " SKSL_RTHEIGHT_NAME
770 " - gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);\n";
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500771 fSetupFragPositionLocal = true;
772 }
773 this->write("sk_FragCoord");
774 }
775}
776
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500777void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
778 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
779 case SK_FRAGCOLOR_BUILTIN:
780 if (fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput()) {
781 this->write("sk_FragColor");
782 } else {
783 this->write("gl_FragColor");
784 }
785 break;
786 case SK_FRAGCOORD_BUILTIN:
787 this->writeFragCoord();
788 break;
Ethan Nicholascd700e92018-08-24 16:43:57 -0400789 case SK_WIDTH_BUILTIN:
790 this->write("u_skRTWidth");
791 break;
792 case SK_HEIGHT_BUILTIN:
793 this->write("u_skRTHeight");
794 break;
Chris Dalton49d14e92018-07-27 12:38:35 -0600795 case SK_CLOCKWISE_BUILTIN:
Chris Daltonc8ece3d2018-07-30 15:03:45 -0600796 this->write(fProgram.fSettings.fFlipY ? "(!gl_FrontFacing)" : "gl_FrontFacing");
Chris Dalton49d14e92018-07-27 12:38:35 -0600797 break;
Chris Daltonb0fd4b12019-10-29 13:41:22 -0600798 case SK_SAMPLEMASK_BUILTIN:
Chris Dalton8a64a442019-10-29 18:54:58 -0600799 SkASSERT(fProgram.fSettings.fCaps->sampleMaskSupport());
Chris Daltonb0fd4b12019-10-29 13:41:22 -0600800 this->write("gl_SampleMask");
801 break;
Ethan Nicholasa51740c2017-02-07 14:53:32 -0500802 case SK_VERTEXID_BUILTIN:
803 this->write("gl_VertexID");
804 break;
Chris Dalton8580d512017-10-14 22:12:33 -0600805 case SK_INSTANCEID_BUILTIN:
806 this->write("gl_InstanceID");
807 break;
Ethan Nicholas67d64602017-02-09 10:15:25 -0500808 case SK_CLIPDISTANCE_BUILTIN:
809 this->write("gl_ClipDistance");
810 break;
Ethan Nicholas52cad152017-02-16 16:37:32 -0500811 case SK_IN_BUILTIN:
812 this->write("gl_in");
813 break;
814 case SK_INVOCATIONID_BUILTIN:
815 this->write("gl_InvocationID");
816 break;
Ethan Nicholaseab2baa2018-04-13 15:16:27 -0400817 case SK_LASTFRAGCOLOR_BUILTIN:
818 this->write(fProgram.fSettings.fCaps->fbFetchColorName());
819 break;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500820 default:
821 this->write(ref.fVariable.fName);
ethannicholas5961bc92016-10-12 06:39:56 -0700822 }
ethannicholasf789b382016-08-03 12:43:36 -0700823}
824
825void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
826 this->writeExpression(*expr.fBase, kPostfix_Precedence);
827 this->write("[");
828 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
829 this->write("]");
830}
831
Brian Osmancd3261a2018-01-16 13:52:29 +0000832bool is_sk_position(const FieldAccess& f) {
833 return "sk_Position" == f.fBase->fType.fields()[f.fFieldIndex].fName;
834}
835
ethannicholasf789b382016-08-03 12:43:36 -0700836void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
837 if (f.fOwnerKind == FieldAccess::kDefault_OwnerKind) {
838 this->writeExpression(*f.fBase, kPostfix_Precedence);
839 this->write(".");
840 }
Ethan Nicholas67d64602017-02-09 10:15:25 -0500841 switch (f.fBase->fType.fields()[f.fFieldIndex].fModifiers.fLayout.fBuiltin) {
842 case SK_CLIPDISTANCE_BUILTIN:
843 this->write("gl_ClipDistance");
844 break;
845 default:
Ethan Nicholasbed683a2017-09-26 14:23:59 -0400846 StringFragment name = f.fBase->fType.fields()[f.fFieldIndex].fName;
847 if (name == "sk_Position") {
848 this->write("gl_Position");
849 } else if (name == "sk_PointSize") {
850 this->write("gl_PointSize");
851 } else {
852 this->write(f.fBase->fType.fields()[f.fFieldIndex].fName);
853 }
Ethan Nicholas67d64602017-02-09 10:15:25 -0500854 }
ethannicholasf789b382016-08-03 12:43:36 -0700855}
856
Ethan Nicholase455f652019-09-13 12:52:55 -0400857void GLSLCodeGenerator::writeConstantSwizzle(const Swizzle& swizzle, const String& constants) {
858 this->writeType(swizzle.fType);
859 this->write("(");
860 this->write(constants);
861 this->write(")");
862}
863
864void GLSLCodeGenerator::writeSwizzleMask(const Swizzle& swizzle, const String& mask) {
ethannicholasf789b382016-08-03 12:43:36 -0700865 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
866 this->write(".");
Ethan Nicholase455f652019-09-13 12:52:55 -0400867 this->write(mask);
868}
869
870void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
871 const String& mask,
872 GLSLCodeGenerator::SwizzleOrder order) {
873 this->writeType(swizzle.fType);
874 this->write("(");
875 if (order == SwizzleOrder::CONSTANTS_FIRST) {
876 this->write(constants);
877 this->write(", ");
878 this->writeSwizzleMask(swizzle, mask);
879 } else {
880 this->writeSwizzleMask(swizzle, mask);
881 this->write(", ");
882 this->write(constants);
883 }
884 this->write(")");
885}
886
887void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
888 const String& mask, const String& reswizzle) {
889 this->writeSwizzleConstructor(swizzle, constants, mask, SwizzleOrder::MASK_FIRST);
890 this->write(".");
891 this->write(reswizzle);
892}
893
894// Writing a swizzle is complicated due to the handling of constant swizzle components. The most
895// problematic case is a mask like '.r00a'. A naive approach might turn that into
896// 'vec4(base.r, 0, 0, base.a)', but that would cause 'base' to be evaluated twice. We instead
897// group the swizzle mask ('ra') and constants ('0, 0') together and use a secondary swizzle to put
898// them back into the right order, so in this case we end up with something like
899// 'vec4(base4.ra, 0, 0).rbag'.
900void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
901 // has a 1 bit in every position for which the swizzle mask is a constant, so 'r0b1' would
902 // yield binary 0101.
903 int constantBits = 0;
904 String mask;
905 String constants;
906 // compute mask ("ra") and constant ("0, 0") strings, and fill in constantBits
ethannicholasf789b382016-08-03 12:43:36 -0700907 for (int c : swizzle.fComponents) {
Ethan Nicholase455f652019-09-13 12:52:55 -0400908 constantBits <<= 1;
909 switch (c) {
910 case SKSL_SWIZZLE_0:
911 constantBits |= 1;
912 if (constants.length() > 0) {
913 constants += ", ";
914 }
915 constants += "0";
916 break;
917 case SKSL_SWIZZLE_1:
918 constantBits |= 1;
919 if (constants.length() > 0) {
920 constants += ", ";
921 }
922 constants += "1";
923 break;
924 case 0:
925 mask += "x";
926 break;
927 case 1:
928 mask += "y";
929 break;
930 case 2:
931 mask += "z";
932 break;
933 case 3:
934 mask += "w";
935 break;
936 default:
937 SkASSERT(false);
Ethan Nicholasac285b12019-02-12 16:05:18 -0500938 }
939 }
Ethan Nicholase455f652019-09-13 12:52:55 -0400940 switch (swizzle.fComponents.size()) {
941 case 1:
942 if (constantBits == 1) {
943 this->write(constants);
944 }
945 else {
946 this->writeSwizzleMask(swizzle, mask);
947 }
948 break;
949 case 2:
950 switch (constantBits) {
951 case 0: // 00
952 this->writeSwizzleMask(swizzle, mask);
953 break;
954 case 1: // 01
955 this->writeSwizzleConstructor(swizzle, constants, mask,
956 SwizzleOrder::MASK_FIRST);
957 break;
958 case 2: // 10
959 this->writeSwizzleConstructor(swizzle, constants, mask,
960 SwizzleOrder::CONSTANTS_FIRST);
961 break;
962 case 3: // 11
963 this->writeConstantSwizzle(swizzle, constants);
964 break;
965 default:
966 SkASSERT(false);
967 }
968 break;
969 case 3:
970 switch (constantBits) {
971 case 0: // 000
972 this->writeSwizzleMask(swizzle, mask);
973 break;
974 case 1: // 001
975 case 3: // 011
976 this->writeSwizzleConstructor(swizzle, constants, mask,
977 SwizzleOrder::MASK_FIRST);
978 break;
979 case 4: // 100
980 case 6: // 110
981 this->writeSwizzleConstructor(swizzle, constants, mask,
982 SwizzleOrder::CONSTANTS_FIRST);
983 break;
984 case 2: // 010
985 this->writeSwizzleConstructor(swizzle, constants, mask, "xzy");
986 break;
987 case 5: // 101
988 this->writeSwizzleConstructor(swizzle, constants, mask, "yxz");
989 break;
990 case 7: // 111
991 this->writeConstantSwizzle(swizzle, constants);
992 break;
993 }
994 break;
995 case 4:
996 switch (constantBits) {
997 case 0: // 0000
998 this->writeSwizzleMask(swizzle, mask);
999 break;
1000 case 1: // 0001
1001 case 3: // 0011
1002 case 7: // 0111
1003 this->writeSwizzleConstructor(swizzle, constants, mask,
1004 SwizzleOrder::MASK_FIRST);
1005 break;
1006 case 8: // 1000
1007 case 12: // 1100
1008 case 14: // 1110
1009 this->writeSwizzleConstructor(swizzle, constants, mask,
1010 SwizzleOrder::CONSTANTS_FIRST);
1011 break;
1012 case 2: // 0010
1013 this->writeSwizzleConstructor(swizzle, constants, mask, "xywz");
1014 break;
1015 case 4: // 0100
1016 this->writeSwizzleConstructor(swizzle, constants, mask, "xwyz");
1017 break;
1018 case 5: // 0101
1019 this->writeSwizzleConstructor(swizzle, constants, mask, "xzyw");
1020 break;
1021 case 6: // 0110
1022 this->writeSwizzleConstructor(swizzle, constants, mask, "xzwy");
1023 break;
1024 case 9: // 1001
1025 this->writeSwizzleConstructor(swizzle, constants, mask, "zxyw");
1026 break;
1027 case 10: // 1010
1028 this->writeSwizzleConstructor(swizzle, constants, mask, "zxwy");
1029 break;
1030 case 11: // 1011
1031 this->writeSwizzleConstructor(swizzle, constants, mask, "yxzw");
1032 break;
1033 case 13: // 1101
1034 this->writeSwizzleConstructor(swizzle, constants, mask, "yzxw");
1035 break;
1036 case 15: // 1111
1037 this->writeConstantSwizzle(swizzle, constants);
1038 break;
1039 }
ethannicholasf789b382016-08-03 12:43:36 -07001040 }
1041}
1042
Ethan Nicholas762466e2017-06-29 10:03:38 -04001043GLSLCodeGenerator::Precedence GLSLCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
ethannicholasf789b382016-08-03 12:43:36 -07001044 switch (op) {
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001045 case Token::Kind::TK_STAR: // fall through
1046 case Token::Kind::TK_SLASH: // fall through
1047 case Token::Kind::TK_PERCENT: return GLSLCodeGenerator::kMultiplicative_Precedence;
1048 case Token::Kind::TK_PLUS: // fall through
1049 case Token::Kind::TK_MINUS: return GLSLCodeGenerator::kAdditive_Precedence;
1050 case Token::Kind::TK_SHL: // fall through
1051 case Token::Kind::TK_SHR: return GLSLCodeGenerator::kShift_Precedence;
1052 case Token::Kind::TK_LT: // fall through
1053 case Token::Kind::TK_GT: // fall through
1054 case Token::Kind::TK_LTEQ: // fall through
1055 case Token::Kind::TK_GTEQ: return GLSLCodeGenerator::kRelational_Precedence;
1056 case Token::Kind::TK_EQEQ: // fall through
1057 case Token::Kind::TK_NEQ: return GLSLCodeGenerator::kEquality_Precedence;
1058 case Token::Kind::TK_BITWISEAND: return GLSLCodeGenerator::kBitwiseAnd_Precedence;
1059 case Token::Kind::TK_BITWISEXOR: return GLSLCodeGenerator::kBitwiseXor_Precedence;
1060 case Token::Kind::TK_BITWISEOR: return GLSLCodeGenerator::kBitwiseOr_Precedence;
1061 case Token::Kind::TK_LOGICALAND: return GLSLCodeGenerator::kLogicalAnd_Precedence;
1062 case Token::Kind::TK_LOGICALXOR: return GLSLCodeGenerator::kLogicalXor_Precedence;
1063 case Token::Kind::TK_LOGICALOR: return GLSLCodeGenerator::kLogicalOr_Precedence;
1064 case Token::Kind::TK_EQ: // fall through
1065 case Token::Kind::TK_PLUSEQ: // fall through
1066 case Token::Kind::TK_MINUSEQ: // fall through
1067 case Token::Kind::TK_STAREQ: // fall through
1068 case Token::Kind::TK_SLASHEQ: // fall through
1069 case Token::Kind::TK_PERCENTEQ: // fall through
1070 case Token::Kind::TK_SHLEQ: // fall through
1071 case Token::Kind::TK_SHREQ: // fall through
1072 case Token::Kind::TK_LOGICALANDEQ: // fall through
1073 case Token::Kind::TK_LOGICALXOREQ: // fall through
1074 case Token::Kind::TK_LOGICALOREQ: // fall through
1075 case Token::Kind::TK_BITWISEANDEQ: // fall through
1076 case Token::Kind::TK_BITWISEXOREQ: // fall through
1077 case Token::Kind::TK_BITWISEOREQ: return GLSLCodeGenerator::kAssignment_Precedence;
1078 case Token::Kind::TK_COMMA: return GLSLCodeGenerator::kSequence_Precedence;
ethannicholasf789b382016-08-03 12:43:36 -07001079 default: ABORT("unsupported binary operator");
1080 }
1081}
1082
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001083void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
ethannicholasf789b382016-08-03 12:43:36 -07001084 Precedence parentPrecedence) {
Adrienne Walkerc02165f2018-08-21 11:08:11 -07001085 if (fProgram.fSettings.fCaps->unfoldShortCircuitAsTernary() &&
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001086 (b.fOperator == Token::Kind::TK_LOGICALAND ||
1087 b.fOperator == Token::Kind::TK_LOGICALOR)) {
Adrienne Walkerc02165f2018-08-21 11:08:11 -07001088 this->writeShortCircuitWorkaroundExpression(b, parentPrecedence);
1089 return;
1090 }
1091
Ethan Nicholas762466e2017-06-29 10:03:38 -04001092 Precedence precedence = GetBinaryPrecedence(b.fOperator);
ethannicholasf789b382016-08-03 12:43:36 -07001093 if (precedence >= parentPrecedence) {
1094 this->write("(");
1095 }
Ethan Nicholas0b631962018-07-24 13:41:11 -04001096 bool positionWorkaround = fProgramKind == Program::Kind::kVertex_Kind &&
1097 Compiler::IsAssignment(b.fOperator) &&
Brian Osmancd3261a2018-01-16 13:52:29 +00001098 Expression::kFieldAccess_Kind == b.fLeft->fKind &&
1099 is_sk_position((FieldAccess&) *b.fLeft) &&
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001100 !b.fRight->containsRTAdjust() &&
Brian Osmancd3261a2018-01-16 13:52:29 +00001101 !fProgram.fSettings.fCaps->canUseFragCoord();
1102 if (positionWorkaround) {
1103 this->write("sk_FragCoord_Workaround = (");
1104 }
ethannicholasf789b382016-08-03 12:43:36 -07001105 this->writeExpression(*b.fLeft, precedence);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001106 this->write(" ");
1107 this->write(Compiler::OperatorName(b.fOperator));
1108 this->write(" ");
ethannicholasf789b382016-08-03 12:43:36 -07001109 this->writeExpression(*b.fRight, precedence);
Brian Osmancd3261a2018-01-16 13:52:29 +00001110 if (positionWorkaround) {
1111 this->write(")");
1112 }
ethannicholasf789b382016-08-03 12:43:36 -07001113 if (precedence >= parentPrecedence) {
1114 this->write(")");
1115 }
1116}
1117
Adrienne Walkerc02165f2018-08-21 11:08:11 -07001118void GLSLCodeGenerator::writeShortCircuitWorkaroundExpression(const BinaryExpression& b,
1119 Precedence parentPrecedence) {
1120 if (kTernary_Precedence >= parentPrecedence) {
1121 this->write("(");
1122 }
1123
1124 // Transform:
1125 // a && b => a ? b : false
1126 // a || b => a ? true : b
1127 this->writeExpression(*b.fLeft, kTernary_Precedence);
1128 this->write(" ? ");
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001129 if (b.fOperator == Token::Kind::TK_LOGICALAND) {
Adrienne Walkerc02165f2018-08-21 11:08:11 -07001130 this->writeExpression(*b.fRight, kTernary_Precedence);
1131 } else {
1132 BoolLiteral boolTrue(fContext, -1, true);
1133 this->writeBoolLiteral(boolTrue);
1134 }
1135 this->write(" : ");
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001136 if (b.fOperator == Token::Kind::TK_LOGICALAND) {
Adrienne Walkerc02165f2018-08-21 11:08:11 -07001137 BoolLiteral boolFalse(fContext, -1, false);
1138 this->writeBoolLiteral(boolFalse);
1139 } else {
1140 this->writeExpression(*b.fRight, kTernary_Precedence);
1141 }
1142 if (kTernary_Precedence >= parentPrecedence) {
1143 this->write(")");
1144 }
1145}
1146
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001147void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
ethannicholasf789b382016-08-03 12:43:36 -07001148 Precedence parentPrecedence) {
1149 if (kTernary_Precedence >= parentPrecedence) {
1150 this->write("(");
1151 }
1152 this->writeExpression(*t.fTest, kTernary_Precedence);
1153 this->write(" ? ");
1154 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
1155 this->write(" : ");
1156 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
1157 if (kTernary_Precedence >= parentPrecedence) {
1158 this->write(")");
1159 }
1160}
1161
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001162void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p,
ethannicholasf789b382016-08-03 12:43:36 -07001163 Precedence parentPrecedence) {
1164 if (kPrefix_Precedence >= parentPrecedence) {
1165 this->write("(");
1166 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001167 this->write(Compiler::OperatorName(p.fOperator));
ethannicholasf789b382016-08-03 12:43:36 -07001168 this->writeExpression(*p.fOperand, kPrefix_Precedence);
1169 if (kPrefix_Precedence >= parentPrecedence) {
1170 this->write(")");
1171 }
1172}
1173
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001174void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p,
ethannicholasf789b382016-08-03 12:43:36 -07001175 Precedence parentPrecedence) {
1176 if (kPostfix_Precedence >= parentPrecedence) {
1177 this->write("(");
1178 }
1179 this->writeExpression(*p.fOperand, kPostfix_Precedence);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001180 this->write(Compiler::OperatorName(p.fOperator));
ethannicholasf789b382016-08-03 12:43:36 -07001181 if (kPostfix_Precedence >= parentPrecedence) {
1182 this->write(")");
1183 }
1184}
1185
1186void GLSLCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
1187 this->write(b.fValue ? "true" : "false");
1188}
1189
1190void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
ethannicholas5961bc92016-10-12 06:39:56 -07001191 if (i.fType == *fContext.fUInt_Type) {
1192 this->write(to_string(i.fValue & 0xffffffff) + "u");
Ethan Nicholas58d56482017-12-19 09:29:22 -05001193 } else if (i.fType == *fContext.fUShort_Type) {
1194 this->write(to_string(i.fValue & 0xffff) + "u");
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001195 } else if (i.fType == *fContext.fUByte_Type) {
1196 this->write(to_string(i.fValue & 0xff) + "u");
1197 } else {
ethannicholas5961bc92016-10-12 06:39:56 -07001198 this->write(to_string((int32_t) i.fValue));
1199 }
ethannicholasf789b382016-08-03 12:43:36 -07001200}
1201
1202void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
1203 this->write(to_string(f.fValue));
1204}
1205
Ethan Nicholas762466e2017-06-29 10:03:38 -04001206void GLSLCodeGenerator::writeSetting(const Setting& s) {
1207 ABORT("internal error; setting was not folded to a constant during compilation\n");
1208}
1209
ethannicholasf789b382016-08-03 12:43:36 -07001210void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -04001211 fSetupFragPositionLocal = false;
1212 fSetupFragCoordWorkaround = false;
Ethan Nicholas00543112018-07-31 09:44:36 -04001213 if (fProgramKind != Program::kPipelineStage_Kind) {
1214 this->writeTypePrecision(f.fDeclaration.fReturnType);
1215 this->writeType(f.fDeclaration.fReturnType);
1216 this->write(" " + f.fDeclaration.fName + "(");
1217 const char* separator = "";
1218 for (const auto& param : f.fDeclaration.fParameters) {
1219 this->write(separator);
1220 separator = ", ";
1221 this->writeModifiers(param->fModifiers, false);
1222 std::vector<int> sizes;
1223 const Type* type = &param->fType;
1224 while (type->kind() == Type::kArray_Kind) {
1225 sizes.push_back(type->columns());
1226 type = &type->componentType();
1227 }
1228 this->writeTypePrecision(*type);
1229 this->writeType(*type);
1230 this->write(" " + param->fName);
1231 for (int s : sizes) {
1232 if (s <= 0) {
1233 this->write("[]");
1234 } else {
1235 this->write("[" + to_string(s) + "]");
1236 }
ethannicholas5961bc92016-10-12 06:39:56 -07001237 }
1238 }
Ethan Nicholas00543112018-07-31 09:44:36 -04001239 this->writeLine(") {");
1240 fIndentation++;
ethannicholasf789b382016-08-03 12:43:36 -07001241 }
ethannicholas5961bc92016-10-12 06:39:56 -07001242 fFunctionHeader = "";
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001243 OutputStream* oldOut = fOut;
1244 StringStream buffer;
ethannicholas5961bc92016-10-12 06:39:56 -07001245 fOut = &buffer;
Ethan Nicholascb670962017-04-20 19:31:52 -04001246 this->writeStatements(((Block&) *f.fBody).fStatements);
Ethan Nicholas00543112018-07-31 09:44:36 -04001247 if (fProgramKind != Program::kPipelineStage_Kind) {
1248 fIndentation--;
1249 this->writeLine("}");
1250 }
ethannicholas5961bc92016-10-12 06:39:56 -07001251
1252 fOut = oldOut;
1253 this->write(fFunctionHeader);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001254 this->write(buffer.str());
ethannicholasf789b382016-08-03 12:43:36 -07001255}
1256
Greg Daniel64773e62016-11-22 09:44:03 -05001257void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
ethannicholas5961bc92016-10-12 06:39:56 -07001258 bool globalContext) {
Brian Salomonf9f45122016-11-29 11:59:17 -05001259 if (modifiers.fFlags & Modifiers::kFlat_Flag) {
1260 this->write("flat ");
1261 }
ethannicholas5961bc92016-10-12 06:39:56 -07001262 if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
1263 this->write("noperspective ");
1264 }
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001265 String layout = modifiers.fLayout.description();
Ethan Nicholas8da9e942017-03-09 16:35:09 -05001266 if (layout.size()) {
1267 this->write(layout + " ");
1268 }
Brian Salomonf9f45122016-11-29 11:59:17 -05001269 if (modifiers.fFlags & Modifiers::kReadOnly_Flag) {
1270 this->write("readonly ");
1271 }
1272 if (modifiers.fFlags & Modifiers::kWriteOnly_Flag) {
1273 this->write("writeonly ");
1274 }
1275 if (modifiers.fFlags & Modifiers::kCoherent_Flag) {
1276 this->write("coherent ");
1277 }
1278 if (modifiers.fFlags & Modifiers::kVolatile_Flag) {
1279 this->write("volatile ");
1280 }
1281 if (modifiers.fFlags & Modifiers::kRestrict_Flag) {
1282 this->write("restrict ");
ethannicholas5961bc92016-10-12 06:39:56 -07001283 }
Greg Daniel64773e62016-11-22 09:44:03 -05001284 if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
ethannicholas5961bc92016-10-12 06:39:56 -07001285 (modifiers.fFlags & Modifiers::kOut_Flag)) {
1286 this->write("inout ");
1287 } else if (modifiers.fFlags & Modifiers::kIn_Flag) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001288 if (globalContext &&
1289 fProgram.fSettings.fCaps->generation() < GrGLSLGeneration::k130_GrGLSLGeneration) {
ethannicholas5961bc92016-10-12 06:39:56 -07001290 this->write(fProgramKind == Program::kVertex_Kind ? "attribute "
1291 : "varying ");
1292 } else {
1293 this->write("in ");
1294 }
1295 } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001296 if (globalContext &&
1297 fProgram.fSettings.fCaps->generation() < GrGLSLGeneration::k130_GrGLSLGeneration) {
ethannicholas5961bc92016-10-12 06:39:56 -07001298 this->write("varying ");
1299 } else {
1300 this->write("out ");
1301 }
1302 }
1303 if (modifiers.fFlags & Modifiers::kUniform_Flag) {
1304 this->write("uniform ");
1305 }
1306 if (modifiers.fFlags & Modifiers::kConst_Flag) {
1307 this->write("const ");
1308 }
Ethan Nicholasa7ceb502019-01-11 10:31:48 -05001309 if (modifiers.fFlags & Modifiers::kPLS_Flag) {
1310 this->write("__pixel_localEXT ");
1311 }
1312 if (modifiers.fFlags & Modifiers::kPLSIn_Flag) {
1313 this->write("__pixel_local_inEXT ");
1314 }
1315 if (modifiers.fFlags & Modifiers::kPLSOut_Flag) {
1316 this->write("__pixel_local_outEXT ");
1317 }
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001318 switch (modifiers.fLayout.fFormat) {
1319 case Layout::Format::kUnspecified:
1320 break;
Robert Phillipsebab03f2019-07-22 08:48:18 -04001321 case Layout::Format::kRGBA32F: // fall through
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001322 case Layout::Format::kR32F:
ethannicholas5961bc92016-10-12 06:39:56 -07001323 this->write("highp ");
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001324 break;
Robert Phillipsebab03f2019-07-22 08:48:18 -04001325 case Layout::Format::kRGBA16F: // fall through
1326 case Layout::Format::kR16F: // fall through
1327 case Layout::Format::kLUMINANCE16F: // fall through
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001328 case Layout::Format::kRG16F:
1329 this->write("mediump ");
1330 break;
Robert Phillipsebab03f2019-07-22 08:48:18 -04001331 case Layout::Format::kRGBA8: // fall through
1332 case Layout::Format::kR8: // fall through
1333 case Layout::Format::kRGBA8I: // fall through
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001334 case Layout::Format::kR8I:
1335 this->write("lowp ");
1336 break;
ethannicholas5961bc92016-10-12 06:39:56 -07001337 }
ethannicholasf789b382016-08-03 12:43:36 -07001338}
1339
1340void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
Ethan Nicholas52cad152017-02-16 16:37:32 -05001341 if (intf.fTypeName == "sk_PerVertex") {
ethannicholasf789b382016-08-03 12:43:36 -07001342 return;
1343 }
ethannicholas5961bc92016-10-12 06:39:56 -07001344 this->writeModifiers(intf.fVariable.fModifiers, true);
Ethan Nicholas50afc172017-02-16 14:49:57 -05001345 this->writeLine(intf.fTypeName + " {");
ethannicholasf789b382016-08-03 12:43:36 -07001346 fIndentation++;
Ethan Nicholas50afc172017-02-16 14:49:57 -05001347 const Type* structType = &intf.fVariable.fType;
1348 while (structType->kind() == Type::kArray_Kind) {
1349 structType = &structType->componentType();
1350 }
1351 for (const auto& f : structType->fields()) {
ethannicholas5961bc92016-10-12 06:39:56 -07001352 this->writeModifiers(f.fModifiers, false);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001353 this->writeTypePrecision(*f.fType);
ethannicholas0730be72016-09-01 07:59:02 -07001354 this->writeType(*f.fType);
ethannicholasf789b382016-08-03 12:43:36 -07001355 this->writeLine(" " + f.fName + ";");
1356 }
1357 fIndentation--;
Ethan Nicholas50afc172017-02-16 14:49:57 -05001358 this->write("}");
1359 if (intf.fInstanceName.size()) {
1360 this->write(" ");
1361 this->write(intf.fInstanceName);
1362 for (const auto& size : intf.fSizes) {
1363 this->write("[");
1364 if (size) {
1365 this->writeExpression(*size, kTopLevel_Precedence);
1366 }
1367 this->write("]");
1368 }
1369 }
1370 this->writeLine(";");
ethannicholasf789b382016-08-03 12:43:36 -07001371}
1372
Ethan Nicholas762466e2017-06-29 10:03:38 -04001373void GLSLCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
1374 this->writeExpression(value, kTopLevel_Precedence);
1375}
1376
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001377const char* GLSLCodeGenerator::getTypePrecision(const Type& type) {
1378 if (usesPrecisionModifiers()) {
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001379 switch (type.kind()) {
1380 case Type::kScalar_Kind:
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001381 if (type == *fContext.fShort_Type || type == *fContext.fUShort_Type ||
1382 type == *fContext.fByte_Type || type == *fContext.fUByte_Type) {
Chris Daltonc2d0dd62018-03-07 07:46:10 -07001383 if (fProgram.fSettings.fForceHighPrecision ||
1384 fProgram.fSettings.fCaps->incompleteShortIntPrecision()) {
1385 return "highp ";
1386 }
1387 return "mediump ";
1388 }
1389 if (type == *fContext.fHalf_Type) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001390 return fProgram.fSettings.fForceHighPrecision ? "highp " : "mediump ";
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001391 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001392 if (type == *fContext.fFloat_Type || type == *fContext.fInt_Type ||
1393 type == *fContext.fUInt_Type) {
1394 return "highp ";
1395 }
1396 return "";
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001397 case Type::kVector_Kind: // fall through
1398 case Type::kMatrix_Kind:
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001399 return this->getTypePrecision(type.componentType());
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001400 default:
1401 break;
1402 }
1403 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001404 return "";
1405}
1406
1407void GLSLCodeGenerator::writeTypePrecision(const Type& type) {
1408 this->write(this->getTypePrecision(type));
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001409}
1410
ethannicholas5961bc92016-10-12 06:39:56 -07001411void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholas14efcbf2017-11-07 09:23:38 -05001412 if (!decl.fVars.size()) {
1413 return;
1414 }
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001415 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001416 for (const auto& stmt : decl.fVars) {
1417 VarDeclaration& var = (VarDeclaration&) *stmt;
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001418 if (wroteType) {
1419 this->write(", ");
1420 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001421 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholasdcba08e2017-08-02 10:52:54 -04001422 this->writeTypePrecision(decl.fBaseType);
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001423 this->writeType(decl.fBaseType);
1424 this->write(" ");
1425 wroteType = true;
1426 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001427 this->write(var.fVar->fName);
1428 for (const auto& size : var.fSizes) {
ethannicholasf789b382016-08-03 12:43:36 -07001429 this->write("[");
ethannicholas5961bc92016-10-12 06:39:56 -07001430 if (size) {
1431 this->writeExpression(*size, kTopLevel_Precedence);
1432 }
ethannicholasf789b382016-08-03 12:43:36 -07001433 this->write("]");
1434 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001435 if (var.fValue) {
ethannicholasf789b382016-08-03 12:43:36 -07001436 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001437 this->writeVarInitializer(*var.fVar, *var.fValue);
ethannicholasf789b382016-08-03 12:43:36 -07001438 }
Brian Osman4b2f9152018-04-17 11:19:57 -04001439 if (!fFoundExternalSamplerDecl && var.fVar->fType == *fContext.fSamplerExternalOES_Type) {
1440 if (fProgram.fSettings.fCaps->externalTextureExtensionString()) {
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001441 this->writeExtension(fProgram.fSettings.fCaps->externalTextureExtensionString());
Brian Osman4b2f9152018-04-17 11:19:57 -04001442 }
Brian Osman061020e2018-04-17 14:22:15 -04001443 if (fProgram.fSettings.fCaps->secondExternalTextureExtensionString()) {
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001444 this->writeExtension(
1445 fProgram.fSettings.fCaps->secondExternalTextureExtensionString());
Brian Osman061020e2018-04-17 14:22:15 -04001446 }
Brian Osman4b2f9152018-04-17 11:19:57 -04001447 fFoundExternalSamplerDecl = true;
1448 }
Brian Salomon67529b22019-08-13 15:31:04 -04001449 if (!fFoundRectSamplerDecl && var.fVar->fType == *fContext.fSampler2DRect_Type) {
1450 fFoundRectSamplerDecl = true;
1451 }
ethannicholasf789b382016-08-03 12:43:36 -07001452 }
Ethan Nicholasb4dc4192017-06-02 10:16:28 -04001453 if (wroteType) {
1454 this->write(";");
1455 }
ethannicholasf789b382016-08-03 12:43:36 -07001456}
1457
1458void GLSLCodeGenerator::writeStatement(const Statement& s) {
1459 switch (s.fKind) {
1460 case Statement::kBlock_Kind:
1461 this->writeBlock((Block&) s);
1462 break;
1463 case Statement::kExpression_Kind:
1464 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
1465 this->write(";");
1466 break;
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001467 case Statement::kReturn_Kind:
ethannicholasf789b382016-08-03 12:43:36 -07001468 this->writeReturnStatement((ReturnStatement&) s);
1469 break;
ethannicholas14fe8cc2016-09-07 13:37:16 -07001470 case Statement::kVarDeclarations_Kind:
ethannicholas5961bc92016-10-12 06:39:56 -07001471 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
ethannicholasf789b382016-08-03 12:43:36 -07001472 break;
1473 case Statement::kIf_Kind:
1474 this->writeIfStatement((IfStatement&) s);
1475 break;
1476 case Statement::kFor_Kind:
1477 this->writeForStatement((ForStatement&) s);
1478 break;
1479 case Statement::kWhile_Kind:
1480 this->writeWhileStatement((WhileStatement&) s);
1481 break;
1482 case Statement::kDo_Kind:
1483 this->writeDoStatement((DoStatement&) s);
1484 break;
Ethan Nicholasaf197692017-02-27 13:26:45 -05001485 case Statement::kSwitch_Kind:
1486 this->writeSwitchStatement((SwitchStatement&) s);
1487 break;
ethannicholasf789b382016-08-03 12:43:36 -07001488 case Statement::kBreak_Kind:
1489 this->write("break;");
1490 break;
1491 case Statement::kContinue_Kind:
1492 this->write("continue;");
1493 break;
1494 case Statement::kDiscard_Kind:
1495 this->write("discard;");
1496 break;
Ethan Nicholascb670962017-04-20 19:31:52 -04001497 case Statement::kNop_Kind:
1498 this->write(";");
1499 break;
ethannicholasf789b382016-08-03 12:43:36 -07001500 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001501#ifdef SK_DEBUG
ethannicholasf789b382016-08-03 12:43:36 -07001502 ABORT("unsupported statement: %s", s.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001503#endif
1504 break;
ethannicholasf789b382016-08-03 12:43:36 -07001505 }
1506}
1507
Ethan Nicholascb670962017-04-20 19:31:52 -04001508void GLSLCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
1509 for (const auto& s : statements) {
1510 if (!s->isEmpty()) {
1511 this->writeStatement(*s);
1512 this->writeLine();
1513 }
1514 }
1515}
1516
ethannicholasf789b382016-08-03 12:43:36 -07001517void GLSLCodeGenerator::writeBlock(const Block& b) {
Ethan Nicholas70728ef2020-05-28 07:09:00 -04001518 if (b.fIsScope) {
1519 this->writeLine("{");
1520 fIndentation++;
1521 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001522 this->writeStatements(b.fStatements);
Ethan Nicholas70728ef2020-05-28 07:09:00 -04001523 if (b.fIsScope) {
1524 fIndentation--;
1525 this->write("}");
1526 }
ethannicholasf789b382016-08-03 12:43:36 -07001527}
1528
1529void GLSLCodeGenerator::writeIfStatement(const IfStatement& stmt) {
1530 this->write("if (");
1531 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
1532 this->write(") ");
1533 this->writeStatement(*stmt.fIfTrue);
1534 if (stmt.fIfFalse) {
1535 this->write(" else ");
1536 this->writeStatement(*stmt.fIfFalse);
1537 }
1538}
1539
1540void GLSLCodeGenerator::writeForStatement(const ForStatement& f) {
1541 this->write("for (");
Ethan Nicholasb310fd52017-06-09 13:46:34 -04001542 if (f.fInitializer && !f.fInitializer->isEmpty()) {
ethannicholasf789b382016-08-03 12:43:36 -07001543 this->writeStatement(*f.fInitializer);
1544 } else {
1545 this->write("; ");
1546 }
1547 if (f.fTest) {
Adrienne Walkeree8295c2018-08-21 10:56:30 -07001548 if (fProgram.fSettings.fCaps->addAndTrueToLoopCondition()) {
1549 std::unique_ptr<Expression> and_true(new BinaryExpression(
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001550 -1, f.fTest->clone(), Token::Kind::TK_LOGICALAND,
Adrienne Walkeree8295c2018-08-21 10:56:30 -07001551 std::unique_ptr<BoolLiteral>(new BoolLiteral(fContext, -1,
1552 true)),
1553 *fContext.fBool_Type));
1554 this->writeExpression(*and_true, kTopLevel_Precedence);
1555 } else {
1556 this->writeExpression(*f.fTest, kTopLevel_Precedence);
1557 }
ethannicholasf789b382016-08-03 12:43:36 -07001558 }
1559 this->write("; ");
1560 if (f.fNext) {
1561 this->writeExpression(*f.fNext, kTopLevel_Precedence);
1562 }
1563 this->write(") ");
1564 this->writeStatement(*f.fStatement);
1565}
1566
1567void GLSLCodeGenerator::writeWhileStatement(const WhileStatement& w) {
1568 this->write("while (");
1569 this->writeExpression(*w.fTest, kTopLevel_Precedence);
1570 this->write(") ");
1571 this->writeStatement(*w.fStatement);
1572}
1573
1574void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
Adrienne Walker8b23ca62018-08-22 10:45:41 -07001575 if (!fProgram.fSettings.fCaps->rewriteDoWhileLoops()) {
1576 this->write("do ");
1577 this->writeStatement(*d.fStatement);
1578 this->write(" while (");
1579 this->writeExpression(*d.fTest, kTopLevel_Precedence);
1580 this->write(");");
1581 return;
1582 }
1583
1584 // Otherwise, do the do while loop workaround, to rewrite loops of the form:
1585 // do {
1586 // CODE;
1587 // } while (CONDITION)
1588 //
1589 // to loops of the form
1590 // bool temp = false;
1591 // while (true) {
1592 // if (temp) {
1593 // if (!CONDITION) {
1594 // break;
1595 // }
1596 // }
1597 // temp = true;
1598 // CODE;
1599 // }
1600 String tmpVar = "_tmpLoopSeenOnce" + to_string(fVarCount++);
1601 this->write("bool ");
1602 this->write(tmpVar);
1603 this->writeLine(" = false;");
1604 this->writeLine("while (true) {");
1605 fIndentation++;
1606 this->write("if (");
1607 this->write(tmpVar);
1608 this->writeLine(") {");
1609 fIndentation++;
1610 this->write("if (!");
1611 this->writeExpression(*d.fTest, kPrefix_Precedence);
1612 this->writeLine(") {");
1613 fIndentation++;
1614 this->writeLine("break;");
1615 fIndentation--;
1616 this->writeLine("}");
1617 fIndentation--;
1618 this->writeLine("}");
1619 this->write(tmpVar);
1620 this->writeLine(" = true;");
ethannicholasf789b382016-08-03 12:43:36 -07001621 this->writeStatement(*d.fStatement);
Adrienne Walker8b23ca62018-08-22 10:45:41 -07001622 this->writeLine();
1623 fIndentation--;
1624 this->write("}");
ethannicholasf789b382016-08-03 12:43:36 -07001625}
1626
Ethan Nicholasaf197692017-02-27 13:26:45 -05001627void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
1628 this->write("switch (");
1629 this->writeExpression(*s.fValue, kTopLevel_Precedence);
1630 this->writeLine(") {");
1631 fIndentation++;
1632 for (const auto& c : s.fCases) {
1633 if (c->fValue) {
1634 this->write("case ");
1635 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1636 this->writeLine(":");
1637 } else {
1638 this->writeLine("default:");
1639 }
1640 fIndentation++;
1641 for (const auto& stmt : c->fStatements) {
1642 this->writeStatement(*stmt);
1643 this->writeLine();
1644 }
1645 fIndentation--;
1646 }
1647 fIndentation--;
1648 this->write("}");
1649}
1650
ethannicholasf789b382016-08-03 12:43:36 -07001651void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1652 this->write("return");
1653 if (r.fExpression) {
1654 this->write(" ");
1655 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1656 }
1657 this->write(";");
1658}
1659
Ethan Nicholas762466e2017-06-29 10:03:38 -04001660void GLSLCodeGenerator::writeHeader() {
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001661 this->write(fProgram.fSettings.fCaps->versionDeclString());
ethannicholasf789b382016-08-03 12:43:36 -07001662 this->writeLine();
Ethan Nicholas762466e2017-06-29 10:03:38 -04001663}
1664
Ethan Nicholas762466e2017-06-29 10:03:38 -04001665void GLSLCodeGenerator::writeProgramElement(const ProgramElement& e) {
1666 switch (e.fKind) {
1667 case ProgramElement::kExtension_Kind:
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001668 this->writeExtension(((Extension&) e).fName);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001669 break;
1670 case ProgramElement::kVar_Kind: {
1671 VarDeclarations& decl = (VarDeclarations&) e;
1672 if (decl.fVars.size() > 0) {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001673 int builtin = ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
Ethan Nicholas762466e2017-06-29 10:03:38 -04001674 if (builtin == -1) {
1675 // normal var
1676 this->writeVarDeclarations(decl, true);
1677 this->writeLine();
1678 } else if (builtin == SK_FRAGCOLOR_BUILTIN &&
Ethan Nicholasa7ceb502019-01-11 10:31:48 -05001679 fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput() &&
1680 ((VarDeclaration&) *decl.fVars[0]).fVar->fWriteCount) {
Brian Salomondc092132018-04-04 10:14:16 -04001681 if (fProgram.fSettings.fFragColorIsInOut) {
1682 this->write("inout ");
1683 } else {
1684 this->write("out ");
1685 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001686 if (usesPrecisionModifiers()) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001687 this->write("mediump ");
Mike Klein5ce39722017-06-27 22:52:03 +00001688 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001689 this->writeLine("vec4 sk_FragColor;");
Mike Klein5ce39722017-06-27 22:52:03 +00001690 }
Mike Klein5ce39722017-06-27 22:52:03 +00001691 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001692 break;
Mike Klein5ce39722017-06-27 22:52:03 +00001693 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001694 case ProgramElement::kInterfaceBlock_Kind:
1695 this->writeInterfaceBlock((InterfaceBlock&) e);
1696 break;
1697 case ProgramElement::kFunction_Kind:
1698 this->writeFunction((FunctionDefinition&) e);
1699 break;
Chris Daltonf1b47bb2017-10-06 11:57:51 -06001700 case ProgramElement::kModifiers_Kind: {
1701 const Modifiers& modifiers = ((ModifiersDeclaration&) e).fModifiers;
1702 if (!fFoundGSInvocations && modifiers.fLayout.fInvocations >= 0) {
1703 if (fProgram.fSettings.fCaps->gsInvocationsExtensionString()) {
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001704 this->writeExtension(fProgram.fSettings.fCaps->gsInvocationsExtensionString());
Chris Daltonf1b47bb2017-10-06 11:57:51 -06001705 }
1706 fFoundGSInvocations = true;
1707 }
1708 this->writeModifiers(modifiers, true);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001709 this->writeLine(";");
1710 break;
Chris Daltonf1b47bb2017-10-06 11:57:51 -06001711 }
Ethan Nicholasaae47c82017-11-10 15:34:03 -05001712 case ProgramElement::kEnum_Kind:
1713 break;
Ethan Nicholas762466e2017-06-29 10:03:38 -04001714 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001715#ifdef SK_DEBUG
1716 printf("unsupported program element %s\n", e.description().c_str());
1717#endif
1718 SkASSERT(false);
Ethan Nicholasc0709392017-06-27 11:20:22 -04001719 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001720}
1721
Ethan Nicholascd700e92018-08-24 16:43:57 -04001722void GLSLCodeGenerator::writeInputVars() {
1723 if (fProgram.fInputs.fRTWidth) {
1724 const char* precision = usesPrecisionModifiers() ? "highp " : "";
1725 fGlobals.writeText("uniform ");
1726 fGlobals.writeText(precision);
1727 fGlobals.writeText("float " SKSL_RTWIDTH_NAME ";\n");
1728 }
1729 if (fProgram.fInputs.fRTHeight) {
1730 const char* precision = usesPrecisionModifiers() ? "highp " : "";
1731 fGlobals.writeText("uniform ");
1732 fGlobals.writeText(precision);
1733 fGlobals.writeText("float " SKSL_RTHEIGHT_NAME ";\n");
1734 }
1735}
1736
Ethan Nicholas762466e2017-06-29 10:03:38 -04001737bool GLSLCodeGenerator::generateCode() {
Ethan Nicholas00543112018-07-31 09:44:36 -04001738 if (fProgramKind != Program::kPipelineStage_Kind) {
1739 this->writeHeader();
1740 }
Chris Dalton8fd79552018-01-11 00:46:14 -05001741 if (Program::kGeometry_Kind == fProgramKind &&
1742 fProgram.fSettings.fCaps->geometryShaderExtensionString()) {
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001743 this->writeExtension(fProgram.fSettings.fCaps->geometryShaderExtensionString());
Chris Dalton8fd79552018-01-11 00:46:14 -05001744 }
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001745 OutputStream* rawOut = fOut;
Ethan Nicholas762466e2017-06-29 10:03:38 -04001746 StringStream body;
1747 fOut = &body;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001748 for (const auto& e : fProgram) {
1749 this->writeProgramElement(e);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001750 }
1751 fOut = rawOut;
ethannicholasddb37d62016-10-20 09:54:00 -07001752
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001753 write_stringstream(fExtensions, *rawOut);
Ethan Nicholascd700e92018-08-24 16:43:57 -04001754 this->writeInputVars();
Ethan Nicholas88f6d372018-07-27 10:03:46 -04001755 write_stringstream(fGlobals, *rawOut);
Brian Osmancc10d792018-07-20 13:09:45 -04001756
1757 if (!fProgram.fSettings.fCaps->canUseFragCoord()) {
1758 Layout layout;
1759 switch (fProgram.fKind) {
1760 case Program::kVertex_Kind: {
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001761 Modifiers modifiers(layout, Modifiers::kOut_Flag);
Brian Osmancc10d792018-07-20 13:09:45 -04001762 this->writeModifiers(modifiers, true);
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001763 if (this->usesPrecisionModifiers()) {
1764 this->write("highp ");
1765 }
Brian Osmancc10d792018-07-20 13:09:45 -04001766 this->write("vec4 sk_FragCoord_Workaround;\n");
1767 break;
1768 }
1769 case Program::kFragment_Kind: {
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001770 Modifiers modifiers(layout, Modifiers::kIn_Flag);
Brian Osmancc10d792018-07-20 13:09:45 -04001771 this->writeModifiers(modifiers, true);
Ethan Nicholas858fecc2019-03-07 13:19:18 -05001772 if (this->usesPrecisionModifiers()) {
1773 this->write("highp ");
1774 }
Brian Osmancc10d792018-07-20 13:09:45 -04001775 this->write("vec4 sk_FragCoord_Workaround;\n");
1776 break;
1777 }
1778 default:
1779 break;
1780 }
1781 }
1782
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001783 if (this->usesPrecisionModifiers()) {
1784 this->writeLine("precision mediump float;");
Brian Salomon67529b22019-08-13 15:31:04 -04001785 this->writeLine("precision mediump sampler2D;");
Brian Salomon5a5f3e82019-08-16 15:05:40 -04001786 if (fFoundExternalSamplerDecl &&
1787 !fProgram.fSettings.fCaps->noDefaultPrecisionForExternalSamplers()) {
Brian Salomon67529b22019-08-13 15:31:04 -04001788 this->writeLine("precision mediump samplerExternalOES;");
1789 }
1790 if (fFoundRectSamplerDecl) {
1791 this->writeLine("precision mediump sampler2DRect;");
1792 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001793 }
Ethan Nicholas6e6525c2018-01-03 17:03:56 -05001794 write_stringstream(fExtraFunctions, *rawOut);
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001795 write_stringstream(body, *rawOut);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001796 return true;
ethannicholasf789b382016-08-03 12:43:36 -07001797}
1798
1799}