blob: 13c171a1a8e2a63ae210a1ec53cfa55ac4fd1529 [file] [log] [blame]
Ethan Nicholascc305772017-10-13 16:17:45 -04001/*
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 */
7
8#include "SkSLMetalCodeGenerator.h"
9
10#include "SkSLCompiler.h"
11#include "ir/SkSLExpressionStatement.h"
12#include "ir/SkSLExtension.h"
13#include "ir/SkSLIndexExpression.h"
14#include "ir/SkSLModifiersDeclaration.h"
15#include "ir/SkSLNop.h"
16#include "ir/SkSLVariableReference.h"
17
Timothy Liangb8eeb802018-07-23 16:46:16 -040018#ifdef SK_MOLTENVK
19 static const uint32_t MVKMagicNum = 0x19960412;
20#endif
Timothy Lianga06f2152018-05-24 15:33:31 -040021
Ethan Nicholascc305772017-10-13 16:17:45 -040022namespace SkSL {
23
Timothy Liangee84fe12018-05-18 14:38:19 -040024void MetalCodeGenerator::setupIntrinsics() {
Timothy Liang7d637782018-06-05 09:58:07 -040025#define METAL(x) std::make_pair(kMetal_IntrinsicKind, k ## x ## _MetalIntrinsic)
26#define SPECIAL(x) std::make_pair(kSpecial_IntrinsicKind, k ## x ## _SpecialIntrinsic)
Timothy Lianga06f2152018-05-24 15:33:31 -040027 fIntrinsicMap[String("texture")] = SPECIAL(Texture);
Timothy Liang651286f2018-06-07 09:55:33 -040028 fIntrinsicMap[String("mod")] = SPECIAL(Mod);
Timothy Lianga06f2152018-05-24 15:33:31 -040029 fIntrinsicMap[String("lessThan")] = METAL(LessThan);
30 fIntrinsicMap[String("lessThanEqual")] = METAL(LessThanEqual);
31 fIntrinsicMap[String("greaterThan")] = METAL(GreaterThan);
32 fIntrinsicMap[String("greaterThanEqual")] = METAL(GreaterThanEqual);
Timothy Liangee84fe12018-05-18 14:38:19 -040033}
34
Ethan Nicholascc305772017-10-13 16:17:45 -040035void MetalCodeGenerator::write(const char* s) {
36 if (!s[0]) {
37 return;
38 }
39 if (fAtLineStart) {
40 for (int i = 0; i < fIndentation; i++) {
41 fOut->writeText(" ");
42 }
43 }
44 fOut->writeText(s);
45 fAtLineStart = false;
46}
47
48void MetalCodeGenerator::writeLine(const char* s) {
49 this->write(s);
50 fOut->writeText(fLineEnding);
51 fAtLineStart = true;
52}
53
54void MetalCodeGenerator::write(const String& s) {
55 this->write(s.c_str());
56}
57
58void MetalCodeGenerator::writeLine(const String& s) {
59 this->writeLine(s.c_str());
60}
61
62void MetalCodeGenerator::writeLine() {
63 this->writeLine("");
64}
65
66void MetalCodeGenerator::writeExtension(const Extension& ext) {
67 this->writeLine("#extension " + ext.fName + " : enable");
68}
69
70void MetalCodeGenerator::writeType(const Type& type) {
71 switch (type.kind()) {
72 case Type::kStruct_Kind:
73 for (const Type* search : fWrittenStructs) {
74 if (*search == type) {
75 // already written
76 this->write(type.name());
77 return;
78 }
79 }
80 fWrittenStructs.push_back(&type);
81 this->writeLine("struct " + type.name() + " {");
82 fIndentation++;
Timothy Liangdc89f192018-06-13 09:20:31 -040083 this->writeFields(type.fields(), type.fOffset);
Ethan Nicholascc305772017-10-13 16:17:45 -040084 fIndentation--;
85 this->write("}");
86 break;
87 case Type::kVector_Kind:
88 this->writeType(type.componentType());
89 this->write(to_string(type.columns()));
90 break;
Timothy Liang43d225f2018-07-19 15:27:13 -040091 case Type::kMatrix_Kind:
92 this->writeType(type.componentType());
93 this->write(to_string(type.columns()));
94 this->write("x");
95 this->write(to_string(type.rows()));
96 break;
Timothy Liangee84fe12018-05-18 14:38:19 -040097 case Type::kSampler_Kind:
Timothy Liang43d225f2018-07-19 15:27:13 -040098 this->write("texture2d<float> "); // FIXME - support other texture types;
Timothy Liangee84fe12018-05-18 14:38:19 -040099 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400100 default:
Timothy Liang43d225f2018-07-19 15:27:13 -0400101 if (type == *fContext.fHalf_Type) {
102 // FIXME - Currently only supporting floats in MSL to avoid type coercion issues.
103 this->write(fContext.fFloat_Type->name());
104 } else if (type == *fContext.fByte_Type) {
105 this->write("char");
106 } else if (type == *fContext.fUByte_Type) {
107 this->write("uchar");
Timothy Liang7d637782018-06-05 09:58:07 -0400108 } else {
109 this->write(type.name());
110 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400111 }
112}
113
114void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
115 switch (expr.fKind) {
116 case Expression::kBinary_Kind:
117 this->writeBinaryExpression((BinaryExpression&) expr, parentPrecedence);
118 break;
119 case Expression::kBoolLiteral_Kind:
120 this->writeBoolLiteral((BoolLiteral&) expr);
121 break;
122 case Expression::kConstructor_Kind:
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500123 this->writeConstructor((Constructor&) expr, parentPrecedence);
Ethan Nicholascc305772017-10-13 16:17:45 -0400124 break;
125 case Expression::kIntLiteral_Kind:
126 this->writeIntLiteral((IntLiteral&) expr);
127 break;
128 case Expression::kFieldAccess_Kind:
129 this->writeFieldAccess(((FieldAccess&) expr));
130 break;
131 case Expression::kFloatLiteral_Kind:
132 this->writeFloatLiteral(((FloatLiteral&) expr));
133 break;
134 case Expression::kFunctionCall_Kind:
135 this->writeFunctionCall((FunctionCall&) expr);
136 break;
137 case Expression::kPrefix_Kind:
138 this->writePrefixExpression((PrefixExpression&) expr, parentPrecedence);
139 break;
140 case Expression::kPostfix_Kind:
141 this->writePostfixExpression((PostfixExpression&) expr, parentPrecedence);
142 break;
143 case Expression::kSetting_Kind:
144 this->writeSetting((Setting&) expr);
145 break;
146 case Expression::kSwizzle_Kind:
147 this->writeSwizzle((Swizzle&) expr);
148 break;
149 case Expression::kVariableReference_Kind:
150 this->writeVariableReference((VariableReference&) expr);
151 break;
152 case Expression::kTernary_Kind:
153 this->writeTernaryExpression((TernaryExpression&) expr, parentPrecedence);
154 break;
155 case Expression::kIndex_Kind:
156 this->writeIndexExpression((IndexExpression&) expr);
157 break;
158 default:
159 ABORT("unsupported expression: %s", expr.description().c_str());
160 }
161}
162
Timothy Liang6403b0e2018-05-17 10:40:04 -0400163void MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c) {
Timothy Liang7d637782018-06-05 09:58:07 -0400164 auto i = fIntrinsicMap.find(c.fFunction.fName);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400165 SkASSERT(i != fIntrinsicMap.end());
Timothy Liang7d637782018-06-05 09:58:07 -0400166 Intrinsic intrinsic = i->second;
167 int32_t intrinsicId = intrinsic.second;
168 switch (intrinsic.first) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400169 case kSpecial_IntrinsicKind:
170 return this->writeSpecialIntrinsic(c, (SpecialIntrinsic) intrinsicId);
Timothy Lianga06f2152018-05-24 15:33:31 -0400171 break;
172 case kMetal_IntrinsicKind:
173 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
174 switch ((MetalIntrinsic) intrinsicId) {
175 case kLessThan_MetalIntrinsic:
176 this->write(" < ");
177 break;
178 case kLessThanEqual_MetalIntrinsic:
179 this->write(" <= ");
180 break;
181 case kGreaterThan_MetalIntrinsic:
182 this->write(" > ");
183 break;
184 case kGreaterThanEqual_MetalIntrinsic:
185 this->write(" >= ");
186 break;
187 default:
188 ABORT("unsupported metal intrinsic kind");
189 }
190 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
191 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400192 default:
193 ABORT("unsupported intrinsic kind");
194 }
195}
196
Ethan Nicholascc305772017-10-13 16:17:45 -0400197void MetalCodeGenerator::writeFunctionCall(const FunctionCall& c) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400198 const auto& entry = fIntrinsicMap.find(c.fFunction.fName);
199 if (entry != fIntrinsicMap.end()) {
200 this->writeIntrinsicCall(c);
201 return;
202 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400203 if (c.fFunction.fBuiltin && "atan" == c.fFunction.fName && 2 == c.fArguments.size()) {
204 this->write("atan2");
Timothy Lianga06f2152018-05-24 15:33:31 -0400205 } else if (c.fFunction.fBuiltin && "inversesqrt" == c.fFunction.fName) {
206 this->write("rsqrt");
Chris Daltondba7aab2018-11-15 10:57:49 -0500207 } else if (c.fFunction.fBuiltin && "inverse" == c.fFunction.fName) {
208 SkASSERT(c.fArguments.size() == 1);
209 this->writeInverseHack(*c.fArguments[0]);
Timothy Liang7d637782018-06-05 09:58:07 -0400210 } else if (c.fFunction.fBuiltin && "dFdx" == c.fFunction.fName) {
211 this->write("dfdx");
212 } else if (c.fFunction.fBuiltin && "dFdy" == c.fFunction.fName) {
213 this->write("dfdy");
Ethan Nicholascc305772017-10-13 16:17:45 -0400214 } else {
Timothy Liang651286f2018-06-07 09:55:33 -0400215 this->writeName(c.fFunction.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400216 }
217 this->write("(");
218 const char* separator = "";
219 if (this->requirements(c.fFunction) & kInputs_Requirement) {
220 this->write("_in");
221 separator = ", ";
222 }
223 if (this->requirements(c.fFunction) & kOutputs_Requirement) {
224 this->write(separator);
225 this->write("_out");
226 separator = ", ";
227 }
228 if (this->requirements(c.fFunction) & kUniforms_Requirement) {
229 this->write(separator);
230 this->write("_uniforms");
231 separator = ", ";
232 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400233 if (this->requirements(c.fFunction) & kGlobals_Requirement) {
234 this->write(separator);
235 this->write("_globals");
236 separator = ", ";
237 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400238 for (size_t i = 0; i < c.fArguments.size(); ++i) {
239 const Expression& arg = *c.fArguments[i];
240 this->write(separator);
241 separator = ", ";
242 if (c.fFunction.fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag) {
243 this->write("&");
244 }
245 this->writeExpression(arg, kSequence_Precedence);
246 }
247 this->write(")");
248}
249
Chris Daltondba7aab2018-11-15 10:57:49 -0500250void MetalCodeGenerator::writeInverseHack(const Expression& mat) {
251 String name = "ERROR_MatrixInverseNotImplementedFor_" + mat.fType.name();
252 if (mat.fType == *fContext.fFloat2x2_Type) {
253 name = "_inverse2";
254 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
255 fWrittenIntrinsics.insert(name);
256 fExtraFunctions.writeText((
257 "float2x2 " + name + "(float2x2 m) {"
258 " return float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]) * (1/determinant(m));"
259 "}"
260 ).c_str());
261 }
262 }
263 this->write(name);
264}
265
Timothy Liang6403b0e2018-05-17 10:40:04 -0400266void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
267 switch (kind) {
268 case kTexture_SpecialIntrinsic:
Timothy Liangee84fe12018-05-18 14:38:19 -0400269 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
Timothy Lianga06f2152018-05-24 15:33:31 -0400270 this->write(".sample(");
271 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
272 this->write(SAMPLER_SUFFIX);
273 this->write(", ");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400274 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
Timothy Liangee84fe12018-05-18 14:38:19 -0400275 if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
276 this->write(".xy)"); // FIXME - add projection functionality
277 } else {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400278 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
Timothy Liangee84fe12018-05-18 14:38:19 -0400279 this->write(")");
280 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400281 break;
Timothy Liang651286f2018-06-07 09:55:33 -0400282 case kMod_SpecialIntrinsic:
283 // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
284 this->write("((");
285 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
286 this->write(") - (");
287 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
288 this->write(") * floor((");
289 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
290 this->write(") / (");
291 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
292 this->write(")))");
293 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400294 default:
295 ABORT("unsupported special intrinsic kind");
296 }
297}
298
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500299// If it hasn't already been written, writes a constructor for 'matrix' which takes a single value
300// of type 'arg'.
301String MetalCodeGenerator::getMatrixConstructHelper(const Type& matrix, const Type& arg) {
302 String key = matrix.name() + arg.name();
303 auto found = fMatrixConstructHelpers.find(key);
304 if (found != fMatrixConstructHelpers.end()) {
305 return found->second;
Ethan Nicholascc305772017-10-13 16:17:45 -0400306 }
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500307 String name;
308 int columns = matrix.columns();
309 int rows = matrix.rows();
310 if (arg.isNumber()) {
311 // creating a matrix from a single scalar value
312 name = "float" + to_string(columns) + "x" + to_string(rows) + "_from_float";
313 fExtraFunctions.printf("float%dx%d %s(float x) {\n",
314 columns, rows, name.c_str());
315 fExtraFunctions.printf(" return float%dx%d(", columns, rows);
316 for (int i = 0; i < columns; ++i) {
317 if (i > 0) {
318 fExtraFunctions.writeText(", ");
319 }
320 fExtraFunctions.printf("float%d(", rows);
321 for (int j = 0; j < rows; ++j) {
322 if (j > 0) {
323 fExtraFunctions.writeText(", ");
324 }
325 if (i == j) {
326 fExtraFunctions.writeText("x");
327 } else {
328 fExtraFunctions.writeText("0");
329 }
330 }
331 fExtraFunctions.writeText(")");
332 }
333 fExtraFunctions.writeText(");\n}\n");
334 }
335 else if (matrix.rows() == 2 && matrix.columns() == 2) {
336 // float2x2(float4) doesn't work, need to split it into float2x2(float2, float2)
337 name = "float2x2_from_float4";
338 fExtraFunctions.printf(
339 "float2x2 %s(float4 v) {\n"
340 " return float2x2(float2(v[0], v[1]), float2(v[2], v[3]));\n"
341 "}\n",
342 name.c_str()
343 );
344 }
345 else {
346 SkASSERT(false);
347 name = "<error>";
348 }
349 fMatrixConstructHelpers[key] = name;
350 return name;
351}
352
353bool MetalCodeGenerator::canCoerce(const Type& t1, const Type& t2) {
354 if (t1.columns() != t2.columns() || t1.rows() != t2.rows()) {
355 return false;
356 }
357 if (t1.columns() > 1) {
358 return this->canCoerce(t1.componentType(), t2.componentType());
359 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500360 return t1.isFloat() && t2.isFloat();
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500361}
362
363void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
364 if (c.fArguments.size() == 1 && this->canCoerce(c.fType, c.fArguments[0]->fType)) {
365 this->writeExpression(*c.fArguments[0], parentPrecedence);
366 return;
367 }
368 if (c.fType.kind() == Type::kMatrix_Kind && c.fArguments.size() == 1) {
369 const Expression& arg = *c.fArguments[0];
370 String name = this->getMatrixConstructHelper(c.fType, arg.fType);
371 this->write(name);
372 this->write("(");
373 this->writeExpression(arg, kSequence_Precedence);
374 this->write(")");
375 } else {
376 this->writeType(c.fType);
377 this->write("(");
378 const char* separator = "";
379 int scalarCount = 0;
380 for (const auto& arg : c.fArguments) {
381 this->write(separator);
382 separator = ", ";
383 if (Type::kMatrix_Kind == c.fType.kind() && Type::kScalar_Kind == arg->fType.kind()) {
384 // float2x2(float, float, float, float) doesn't work in Metal 1, so we need to merge
385 // to float2x2(float2, float2).
386 if (!scalarCount) {
387 this->writeType(c.fType.componentType());
388 this->write(to_string(c.fType.rows()));
389 this->write("(");
390 }
391 ++scalarCount;
392 }
393 this->writeExpression(*arg, kSequence_Precedence);
394 if (scalarCount && scalarCount == c.fType.rows()) {
395 this->write(")");
396 scalarCount = 0;
397 }
398 }
399 this->write(")");
400 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400401}
402
403void MetalCodeGenerator::writeFragCoord() {
Ethan Nicholas20798e52018-12-04 12:30:50 -0500404 this->write("float4(_fragCoord.x, _anonInterface0.u_skRTHeight - _fragCoord.y, 0.0, "
405 "_fragCoord.w)");
Ethan Nicholascc305772017-10-13 16:17:45 -0400406}
407
408void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
409 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
410 case SK_FRAGCOLOR_BUILTIN:
Timothy Liang7d637782018-06-05 09:58:07 -0400411 this->write("_out->sk_FragColor");
Ethan Nicholascc305772017-10-13 16:17:45 -0400412 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400413 case SK_FRAGCOORD_BUILTIN:
414 this->writeFragCoord();
415 break;
Timothy Liangdc89f192018-06-13 09:20:31 -0400416 case SK_VERTEXID_BUILTIN:
417 this->write("sk_VertexID");
418 break;
419 case SK_INSTANCEID_BUILTIN:
420 this->write("sk_InstanceID");
421 break;
Timothy Liang7b8875d2018-08-10 09:42:31 -0400422 case SK_CLOCKWISE_BUILTIN:
423 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
424 // clockwise to match Skia convention. This is also the default in MoltenVK.
425 this->write(fProgram.fSettings.fFlipY ? "_frontFacing" : "(!_frontFacing)");
426 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400427 default:
428 if (Variable::kGlobal_Storage == ref.fVariable.fStorage) {
429 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
430 this->write("_in.");
431 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400432 this->write("_out->");
Timothy Lianga06f2152018-05-24 15:33:31 -0400433 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
434 ref.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400435 this->write("_uniforms.");
436 } else {
Timothy Liangee84fe12018-05-18 14:38:19 -0400437 this->write("_globals->");
Ethan Nicholascc305772017-10-13 16:17:45 -0400438 }
439 }
Timothy Liang651286f2018-06-07 09:55:33 -0400440 this->writeName(ref.fVariable.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400441 }
442}
443
444void MetalCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
445 this->writeExpression(*expr.fBase, kPostfix_Precedence);
446 this->write("[");
447 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
448 this->write("]");
449}
450
451void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
Timothy Liang7d637782018-06-05 09:58:07 -0400452 const Type::Field* field = &f.fBase->fType.fields()[f.fFieldIndex];
Ethan Nicholascc305772017-10-13 16:17:45 -0400453 if (FieldAccess::kDefault_OwnerKind == f.fOwnerKind) {
454 this->writeExpression(*f.fBase, kPostfix_Precedence);
455 this->write(".");
456 }
Timothy Liang7d637782018-06-05 09:58:07 -0400457 switch (field->fModifiers.fLayout.fBuiltin) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400458 case SK_CLIPDISTANCE_BUILTIN:
459 this->write("gl_ClipDistance");
460 break;
461 case SK_POSITION_BUILTIN:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400462 this->write("_out->sk_Position");
Ethan Nicholascc305772017-10-13 16:17:45 -0400463 break;
464 default:
Timothy Liang7d637782018-06-05 09:58:07 -0400465 if (field->fName == "sk_PointSize") {
466 this->write("_out->sk_PointSize");
467 } else {
468 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
469 this->write("_globals->");
470 this->write(fInterfaceBlockNameMap[fInterfaceBlockMap[field]]);
471 this->write("->");
472 }
Timothy Liang651286f2018-06-07 09:55:33 -0400473 this->writeName(field->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400474 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400475 }
476}
477
478void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
479 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
480 this->write(".");
481 for (int c : swizzle.fComponents) {
482 this->write(&("x\0y\0z\0w\0"[c * 2]));
483 }
484}
485
486MetalCodeGenerator::Precedence MetalCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
487 switch (op) {
488 case Token::STAR: // fall through
489 case Token::SLASH: // fall through
490 case Token::PERCENT: return MetalCodeGenerator::kMultiplicative_Precedence;
491 case Token::PLUS: // fall through
492 case Token::MINUS: return MetalCodeGenerator::kAdditive_Precedence;
493 case Token::SHL: // fall through
494 case Token::SHR: return MetalCodeGenerator::kShift_Precedence;
495 case Token::LT: // fall through
496 case Token::GT: // fall through
497 case Token::LTEQ: // fall through
498 case Token::GTEQ: return MetalCodeGenerator::kRelational_Precedence;
499 case Token::EQEQ: // fall through
500 case Token::NEQ: return MetalCodeGenerator::kEquality_Precedence;
501 case Token::BITWISEAND: return MetalCodeGenerator::kBitwiseAnd_Precedence;
502 case Token::BITWISEXOR: return MetalCodeGenerator::kBitwiseXor_Precedence;
503 case Token::BITWISEOR: return MetalCodeGenerator::kBitwiseOr_Precedence;
504 case Token::LOGICALAND: return MetalCodeGenerator::kLogicalAnd_Precedence;
505 case Token::LOGICALXOR: return MetalCodeGenerator::kLogicalXor_Precedence;
506 case Token::LOGICALOR: return MetalCodeGenerator::kLogicalOr_Precedence;
507 case Token::EQ: // fall through
508 case Token::PLUSEQ: // fall through
509 case Token::MINUSEQ: // fall through
510 case Token::STAREQ: // fall through
511 case Token::SLASHEQ: // fall through
512 case Token::PERCENTEQ: // fall through
513 case Token::SHLEQ: // fall through
514 case Token::SHREQ: // fall through
515 case Token::LOGICALANDEQ: // fall through
516 case Token::LOGICALXOREQ: // fall through
517 case Token::LOGICALOREQ: // fall through
518 case Token::BITWISEANDEQ: // fall through
519 case Token::BITWISEXOREQ: // fall through
520 case Token::BITWISEOREQ: return MetalCodeGenerator::kAssignment_Precedence;
521 case Token::COMMA: return MetalCodeGenerator::kSequence_Precedence;
522 default: ABORT("unsupported binary operator");
523 }
524}
525
526void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
527 Precedence parentPrecedence) {
528 Precedence precedence = GetBinaryPrecedence(b.fOperator);
529 if (precedence >= parentPrecedence) {
530 this->write("(");
531 }
532 if (Compiler::IsAssignment(b.fOperator) &&
533 Expression::kVariableReference_Kind == b.fLeft->fKind &&
534 Variable::kParameter_Storage == ((VariableReference&) *b.fLeft).fVariable.fStorage &&
535 (((VariableReference&) *b.fLeft).fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) {
536 // writing to an out parameter. Since we have to turn those into pointers, we have to
537 // dereference it here.
538 this->write("*");
539 }
540 this->writeExpression(*b.fLeft, precedence);
541 if (b.fOperator != Token::EQ && Compiler::IsAssignment(b.fOperator) &&
542 Expression::kSwizzle_Kind == b.fLeft->fKind && !b.fLeft->hasSideEffects()) {
543 // This doesn't compile in Metal:
544 // float4 x = float4(1);
545 // x.xy *= float2x2(...);
546 // with the error message "non-const reference cannot bind to vector element",
547 // but switching it to x.xy = x.xy * float2x2(...) fixes it. We perform this tranformation
548 // as long as the LHS has no side effects, and hope for the best otherwise.
549 this->write(" = ");
550 this->writeExpression(*b.fLeft, kAssignment_Precedence);
551 this->write(" ");
552 String op = Compiler::OperatorName(b.fOperator);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400553 SkASSERT(op.endsWith("="));
Ethan Nicholascc305772017-10-13 16:17:45 -0400554 this->write(op.substr(0, op.size() - 1).c_str());
555 this->write(" ");
556 } else {
557 this->write(String(" ") + Compiler::OperatorName(b.fOperator) + " ");
558 }
559 this->writeExpression(*b.fRight, precedence);
560 if (precedence >= parentPrecedence) {
561 this->write(")");
562 }
563}
564
565void MetalCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
566 Precedence parentPrecedence) {
567 if (kTernary_Precedence >= parentPrecedence) {
568 this->write("(");
569 }
570 this->writeExpression(*t.fTest, kTernary_Precedence);
571 this->write(" ? ");
572 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
573 this->write(" : ");
574 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
575 if (kTernary_Precedence >= parentPrecedence) {
576 this->write(")");
577 }
578}
579
580void MetalCodeGenerator::writePrefixExpression(const PrefixExpression& p,
581 Precedence parentPrecedence) {
582 if (kPrefix_Precedence >= parentPrecedence) {
583 this->write("(");
584 }
585 this->write(Compiler::OperatorName(p.fOperator));
586 this->writeExpression(*p.fOperand, kPrefix_Precedence);
587 if (kPrefix_Precedence >= parentPrecedence) {
588 this->write(")");
589 }
590}
591
592void MetalCodeGenerator::writePostfixExpression(const PostfixExpression& p,
593 Precedence parentPrecedence) {
594 if (kPostfix_Precedence >= parentPrecedence) {
595 this->write("(");
596 }
597 this->writeExpression(*p.fOperand, kPostfix_Precedence);
598 this->write(Compiler::OperatorName(p.fOperator));
599 if (kPostfix_Precedence >= parentPrecedence) {
600 this->write(")");
601 }
602}
603
604void MetalCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
605 this->write(b.fValue ? "true" : "false");
606}
607
608void MetalCodeGenerator::writeIntLiteral(const IntLiteral& i) {
609 if (i.fType == *fContext.fUInt_Type) {
610 this->write(to_string(i.fValue & 0xffffffff) + "u");
611 } else {
612 this->write(to_string((int32_t) i.fValue));
613 }
614}
615
616void MetalCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
617 this->write(to_string(f.fValue));
618}
619
620void MetalCodeGenerator::writeSetting(const Setting& s) {
621 ABORT("internal error; setting was not folded to a constant during compilation\n");
622}
623
624void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
625 const char* separator = "";
626 if ("main" == f.fDeclaration.fName) {
627 switch (fProgram.fKind) {
628 case Program::kFragment_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400629#ifdef SK_MOLTENVK
630 this->write("fragment Outputs main0");
631#else
632 this->write("fragment Outputs fragmentMain");
633#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400634 break;
635 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400636#ifdef SK_MOLTENVK
Timothy Lianga06f2152018-05-24 15:33:31 -0400637 this->write("vertex Outputs main0");
Timothy Liangb8eeb802018-07-23 16:46:16 -0400638#else
639 this->write("vertex Outputs vertexMain");
640#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400641 break;
642 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400643 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400644 }
645 this->write("(Inputs _in [[stage_in]]");
646 if (-1 != fUniformBuffer) {
647 this->write(", constant Uniforms& _uniforms [[buffer(" +
648 to_string(fUniformBuffer) + ")]]");
649 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400650 for (const auto& e : fProgram) {
651 if (ProgramElement::kVar_Kind == e.fKind) {
652 VarDeclarations& decls = (VarDeclarations&) e;
653 if (!decls.fVars.size()) {
654 continue;
655 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400656 for (const auto& stmt: decls.fVars) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400657 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400658 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
Timothy Liang7d637782018-06-05 09:58:07 -0400659 this->write(", texture2d<float> "); // FIXME - support other texture types
Timothy Liang651286f2018-06-07 09:55:33 -0400660 this->writeName(var.fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400661 this->write("[[texture(");
Timothy Lianga06f2152018-05-24 15:33:31 -0400662 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
663 this->write(")]]");
664 this->write(", sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -0400665 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400666 this->write(SAMPLER_SUFFIX);
667 this->write("[[sampler(");
668 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
Timothy Liangee84fe12018-05-18 14:38:19 -0400669 this->write(")]]");
670 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400671 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400672 } else if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
673 InterfaceBlock& intf = (InterfaceBlock&) e;
674 if ("sk_PerVertex" == intf.fTypeName) {
675 continue;
676 }
677 this->write(", constant ");
678 this->writeType(intf.fVariable.fType);
679 this->write("& " );
680 this->write(fInterfaceBlockNameMap[&intf]);
681 this->write(" [[buffer(");
Timothy Liang057c3902018-08-08 10:48:45 -0400682#ifdef SK_MOLTENVK
Timothy Liang7d637782018-06-05 09:58:07 -0400683 this->write(to_string(intf.fVariable.fModifiers.fLayout.fSet));
Timothy Liang057c3902018-08-08 10:48:45 -0400684#else
685 this->write(to_string(intf.fVariable.fModifiers.fLayout.fBinding));
686#endif
Timothy Lianga06f2152018-05-24 15:33:31 -0400687 this->write(")]]");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400688 }
689 }
Timothy Liang5422f9a2018-08-10 10:57:55 -0400690 if (fProgram.fKind == Program::kFragment_Kind) {
691 if (fInterfaceBlockNameMap.empty()) {
Timothy Liangb8eeb802018-07-23 16:46:16 -0400692 // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
693 // in a different way altogether.
Timothy Liang5422f9a2018-08-10 10:57:55 -0400694#ifdef SK_MOLTENVK
695 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(0)]]");
696#else
697 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
698#endif
699 }
Timothy Liang7b8875d2018-08-10 09:42:31 -0400700 this->write(", bool _frontFacing [[front_facing]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400701 this->write(", float4 _fragCoord [[position]]");
Timothy Liangdc89f192018-06-13 09:20:31 -0400702 } else if (fProgram.fKind == Program::kVertex_Kind) {
703 this->write(", uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400704 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400705 separator = ", ";
706 } else {
707 this->writeType(f.fDeclaration.fReturnType);
Timothy Liang651286f2018-06-07 09:55:33 -0400708 this->write(" ");
709 this->writeName(f.fDeclaration.fName);
710 this->write("(");
Ethan Nicholascc305772017-10-13 16:17:45 -0400711 if (this->requirements(f.fDeclaration) & kInputs_Requirement) {
712 this->write("Inputs _in");
713 separator = ", ";
714 }
715 if (this->requirements(f.fDeclaration) & kOutputs_Requirement) {
716 this->write(separator);
Timothy Liangee84fe12018-05-18 14:38:19 -0400717 this->write("thread Outputs* _out");
Ethan Nicholascc305772017-10-13 16:17:45 -0400718 separator = ", ";
719 }
720 if (this->requirements(f.fDeclaration) & kUniforms_Requirement) {
721 this->write(separator);
722 this->write("Uniforms _uniforms");
723 separator = ", ";
724 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400725 if (this->requirements(f.fDeclaration) & kGlobals_Requirement) {
726 this->write(separator);
727 this->write("thread Globals* _globals");
728 separator = ", ";
729 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400730 }
731 for (const auto& param : f.fDeclaration.fParameters) {
732 this->write(separator);
733 separator = ", ";
734 this->writeModifiers(param->fModifiers, false);
735 std::vector<int> sizes;
736 const Type* type = &param->fType;
737 while (Type::kArray_Kind == type->kind()) {
738 sizes.push_back(type->columns());
739 type = &type->componentType();
740 }
741 this->writeType(*type);
742 if (param->fModifiers.fFlags & Modifiers::kOut_Flag) {
743 this->write("*");
744 }
Timothy Liang651286f2018-06-07 09:55:33 -0400745 this->write(" ");
746 this->writeName(param->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400747 for (int s : sizes) {
748 if (s <= 0) {
749 this->write("[]");
750 } else {
751 this->write("[" + to_string(s) + "]");
752 }
753 }
754 }
755 this->writeLine(") {");
756
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400757 SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
Brian Salomondc092132018-04-04 10:14:16 -0400758
Ethan Nicholascc305772017-10-13 16:17:45 -0400759 if ("main" == f.fDeclaration.fName) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400760 if (fNeedsGlobalStructInit) {
761 this->writeLine(" Globals globalStruct;");
762 this->writeLine(" thread Globals* _globals = &globalStruct;");
Timothy Liang7d637782018-06-05 09:58:07 -0400763 for (const auto& intf: fInterfaceBlockNameMap) {
764 const auto& intfName = intf.second;
765 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400766 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400767 this->write(" = &");
Timothy Liang651286f2018-06-07 09:55:33 -0400768 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400769 this->write(";\n");
770 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400771 for (const auto& var: fInitNonConstGlobalVars) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400772 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400773 this->writeName(var->fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400774 this->write(" = ");
775 this->writeVarInitializer(*var->fVar, *var->fValue);
776 this->writeLine(";");
777 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400778 for (const auto& texture: fTextures) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400779 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400780 this->writeName(texture->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400781 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400782 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400783 this->write(";\n");
784 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400785 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400786 this->write(SAMPLER_SUFFIX);
787 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400788 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400789 this->write(SAMPLER_SUFFIX);
Timothy Liangee84fe12018-05-18 14:38:19 -0400790 this->write(";\n");
791 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400792 }
Timothy Liang7d637782018-06-05 09:58:07 -0400793 this->writeLine(" Outputs _outputStruct;");
794 this->writeLine(" thread Outputs* _out = &_outputStruct;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400795 }
796 fFunctionHeader = "";
797 OutputStream* oldOut = fOut;
798 StringStream buffer;
799 fOut = &buffer;
800 fIndentation++;
801 this->writeStatements(((Block&) *f.fBody).fStatements);
802 if ("main" == f.fDeclaration.fName) {
803 switch (fProgram.fKind) {
804 case Program::kFragment_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -0400805 this->writeLine("return *_out;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400806 break;
807 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400808 this->writeLine("_out->sk_Position.y = -_out->sk_Position.y;");
Timothy Lianga06f2152018-05-24 15:33:31 -0400809 this->writeLine("return *_out;"); // FIXME - detect if function already has return
Ethan Nicholascc305772017-10-13 16:17:45 -0400810 break;
811 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400812 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400813 }
814 }
815 fIndentation--;
816 this->writeLine("}");
817
818 fOut = oldOut;
819 this->write(fFunctionHeader);
820 this->write(buffer.str());
821}
822
823void MetalCodeGenerator::writeModifiers(const Modifiers& modifiers,
824 bool globalContext) {
825 if (modifiers.fFlags & Modifiers::kOut_Flag) {
826 this->write("thread ");
827 }
828 if (modifiers.fFlags & Modifiers::kConst_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400829 this->write("constant ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400830 }
831}
832
833void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
834 if ("sk_PerVertex" == intf.fTypeName) {
835 return;
836 }
837 this->writeModifiers(intf.fVariable.fModifiers, true);
Timothy Liangdc89f192018-06-13 09:20:31 -0400838 this->write("struct ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400839 this->writeLine(intf.fTypeName + " {");
Ethan Nicholascc305772017-10-13 16:17:45 -0400840 const Type* structType = &intf.fVariable.fType;
Timothy Lianga06f2152018-05-24 15:33:31 -0400841 fWrittenStructs.push_back(structType);
Ethan Nicholascc305772017-10-13 16:17:45 -0400842 while (Type::kArray_Kind == structType->kind()) {
843 structType = &structType->componentType();
844 }
Timothy Liangdc89f192018-06-13 09:20:31 -0400845 fIndentation++;
846 writeFields(structType->fields(), structType->fOffset, &intf);
Timothy Liang7d637782018-06-05 09:58:07 -0400847 if (fProgram.fKind == Program::kFragment_Kind) {
848 this->writeLine("float u_skRTHeight;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400849 }
850 fIndentation--;
851 this->write("}");
852 if (intf.fInstanceName.size()) {
853 this->write(" ");
854 this->write(intf.fInstanceName);
855 for (const auto& size : intf.fSizes) {
856 this->write("[");
857 if (size) {
858 this->writeExpression(*size, kTopLevel_Precedence);
859 }
860 this->write("]");
861 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400862 fInterfaceBlockNameMap[&intf] = intf.fInstanceName;
863 } else {
Timothy Liang7d637782018-06-05 09:58:07 -0400864 fInterfaceBlockNameMap[&intf] = "_anonInterface" + to_string(fAnonInterfaceCount++);
Ethan Nicholascc305772017-10-13 16:17:45 -0400865 }
866 this->writeLine(";");
867}
868
Timothy Liangdc89f192018-06-13 09:20:31 -0400869void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int parentOffset,
870 const InterfaceBlock* parentIntf) {
Timothy Liang609fbe32018-08-10 16:40:49 -0400871#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -0400872 MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
Timothy Liang609fbe32018-08-10 16:40:49 -0400873#else
874 MemoryLayout memoryLayout(MemoryLayout::kMetal_Standard);
875#endif
Timothy Liangdc89f192018-06-13 09:20:31 -0400876 int currentOffset = 0;
877 for (const auto& field: fields) {
878 int fieldOffset = field.fModifiers.fLayout.fOffset;
879 const Type* fieldType = field.fType;
880 if (fieldOffset != -1) {
881 if (currentOffset > fieldOffset) {
882 fErrors.error(parentOffset,
883 "offset of field '" + field.fName + "' must be at least " +
884 to_string((int) currentOffset));
885 } else if (currentOffset < fieldOffset) {
886 this->write("char pad");
887 this->write(to_string(fPaddingCount++));
888 this->write("[");
889 this->write(to_string(fieldOffset - currentOffset));
890 this->writeLine("];");
891 currentOffset = fieldOffset;
892 }
893 int alignment = memoryLayout.alignment(*fieldType);
894 if (fieldOffset % alignment) {
895 fErrors.error(parentOffset,
896 "offset of field '" + field.fName + "' must be a multiple of " +
897 to_string((int) alignment));
898 }
899 }
Timothy Liang609fbe32018-08-10 16:40:49 -0400900#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -0400901 if (fieldType->kind() == Type::kVector_Kind &&
902 fieldType->columns() == 3) {
Timothy Liang609fbe32018-08-10 16:40:49 -0400903 SkASSERT(memoryLayout.size(*fieldType) == 3);
Timothy Liangdc89f192018-06-13 09:20:31 -0400904 // Pack all vec3 types so that their size in bytes will match what was expected in the
905 // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
906 // has vec3 equal to 3 * component type.
Timothy Liang609fbe32018-08-10 16:40:49 -0400907
908 // FIXME - Packed vectors can't be accessed by swizzles, but can be indexed into. A
909 // combination of this being a problem which only occurs when using MoltenVK and the
910 // fact that we haven't swizzled a vec3 yet means that this problem hasn't been
911 // addressed.
Timothy Liangdc89f192018-06-13 09:20:31 -0400912 this->write(PACKED_PREFIX);
913 }
Timothy Liang609fbe32018-08-10 16:40:49 -0400914#endif
Timothy Liangdc89f192018-06-13 09:20:31 -0400915 currentOffset += memoryLayout.size(*fieldType);
916 std::vector<int> sizes;
917 while (fieldType->kind() == Type::kArray_Kind) {
918 sizes.push_back(fieldType->columns());
919 fieldType = &fieldType->componentType();
920 }
921 this->writeModifiers(field.fModifiers, false);
922 this->writeType(*fieldType);
923 this->write(" ");
924 this->writeName(field.fName);
925 for (int s : sizes) {
926 if (s <= 0) {
927 this->write("[]");
928 } else {
929 this->write("[" + to_string(s) + "]");
930 }
931 }
932 this->writeLine(";");
933 if (parentIntf) {
934 fInterfaceBlockMap[&field] = parentIntf;
935 }
936 }
937}
938
Ethan Nicholascc305772017-10-13 16:17:45 -0400939void MetalCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
940 this->writeExpression(value, kTopLevel_Precedence);
941}
942
Timothy Liang651286f2018-06-07 09:55:33 -0400943void MetalCodeGenerator::writeName(const String& name) {
944 if (fReservedWords.find(name) != fReservedWords.end()) {
945 this->write("_"); // adding underscore before name to avoid conflict with reserved words
946 }
947 this->write(name);
948}
949
Ethan Nicholascc305772017-10-13 16:17:45 -0400950void MetalCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400951 SkASSERT(decl.fVars.size() > 0);
Ethan Nicholascc305772017-10-13 16:17:45 -0400952 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000953 for (const auto& stmt : decl.fVars) {
954 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400955 if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400956 continue;
957 }
958 if (wroteType) {
959 this->write(", ");
960 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000961 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholascc305772017-10-13 16:17:45 -0400962 this->writeType(decl.fBaseType);
963 this->write(" ");
964 wroteType = true;
965 }
Timothy Liang651286f2018-06-07 09:55:33 -0400966 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000967 for (const auto& size : var.fSizes) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400968 this->write("[");
969 if (size) {
970 this->writeExpression(*size, kTopLevel_Precedence);
971 }
972 this->write("]");
973 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000974 if (var.fValue) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400975 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000976 this->writeVarInitializer(*var.fVar, *var.fValue);
Ethan Nicholascc305772017-10-13 16:17:45 -0400977 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000978 if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400979 if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
980 fHeader.writeText("#extension ");
981 fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
982 fHeader.writeText(" : require\n");
983 }
984 fFoundImageDecl = true;
985 }
986 }
987 if (wroteType) {
988 this->write(";");
989 }
990}
991
992void MetalCodeGenerator::writeStatement(const Statement& s) {
993 switch (s.fKind) {
994 case Statement::kBlock_Kind:
995 this->writeBlock((Block&) s);
996 break;
997 case Statement::kExpression_Kind:
998 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
999 this->write(";");
1000 break;
1001 case Statement::kReturn_Kind:
1002 this->writeReturnStatement((ReturnStatement&) s);
1003 break;
1004 case Statement::kVarDeclarations_Kind:
1005 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
1006 break;
1007 case Statement::kIf_Kind:
1008 this->writeIfStatement((IfStatement&) s);
1009 break;
1010 case Statement::kFor_Kind:
1011 this->writeForStatement((ForStatement&) s);
1012 break;
1013 case Statement::kWhile_Kind:
1014 this->writeWhileStatement((WhileStatement&) s);
1015 break;
1016 case Statement::kDo_Kind:
1017 this->writeDoStatement((DoStatement&) s);
1018 break;
1019 case Statement::kSwitch_Kind:
1020 this->writeSwitchStatement((SwitchStatement&) s);
1021 break;
1022 case Statement::kBreak_Kind:
1023 this->write("break;");
1024 break;
1025 case Statement::kContinue_Kind:
1026 this->write("continue;");
1027 break;
1028 case Statement::kDiscard_Kind:
Timothy Lianga06f2152018-05-24 15:33:31 -04001029 this->write("discard_fragment();");
Ethan Nicholascc305772017-10-13 16:17:45 -04001030 break;
1031 case Statement::kNop_Kind:
1032 this->write(";");
1033 break;
1034 default:
1035 ABORT("unsupported statement: %s", s.description().c_str());
1036 }
1037}
1038
1039void MetalCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
1040 for (const auto& s : statements) {
1041 if (!s->isEmpty()) {
1042 this->writeStatement(*s);
1043 this->writeLine();
1044 }
1045 }
1046}
1047
1048void MetalCodeGenerator::writeBlock(const Block& b) {
1049 this->writeLine("{");
1050 fIndentation++;
1051 this->writeStatements(b.fStatements);
1052 fIndentation--;
1053 this->write("}");
1054}
1055
1056void MetalCodeGenerator::writeIfStatement(const IfStatement& stmt) {
1057 this->write("if (");
1058 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
1059 this->write(") ");
1060 this->writeStatement(*stmt.fIfTrue);
1061 if (stmt.fIfFalse) {
1062 this->write(" else ");
1063 this->writeStatement(*stmt.fIfFalse);
1064 }
1065}
1066
1067void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
1068 this->write("for (");
1069 if (f.fInitializer && !f.fInitializer->isEmpty()) {
1070 this->writeStatement(*f.fInitializer);
1071 } else {
1072 this->write("; ");
1073 }
1074 if (f.fTest) {
1075 this->writeExpression(*f.fTest, kTopLevel_Precedence);
1076 }
1077 this->write("; ");
1078 if (f.fNext) {
1079 this->writeExpression(*f.fNext, kTopLevel_Precedence);
1080 }
1081 this->write(") ");
1082 this->writeStatement(*f.fStatement);
1083}
1084
1085void MetalCodeGenerator::writeWhileStatement(const WhileStatement& w) {
1086 this->write("while (");
1087 this->writeExpression(*w.fTest, kTopLevel_Precedence);
1088 this->write(") ");
1089 this->writeStatement(*w.fStatement);
1090}
1091
1092void MetalCodeGenerator::writeDoStatement(const DoStatement& d) {
1093 this->write("do ");
1094 this->writeStatement(*d.fStatement);
1095 this->write(" while (");
1096 this->writeExpression(*d.fTest, kTopLevel_Precedence);
1097 this->write(");");
1098}
1099
1100void MetalCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
1101 this->write("switch (");
1102 this->writeExpression(*s.fValue, kTopLevel_Precedence);
1103 this->writeLine(") {");
1104 fIndentation++;
1105 for (const auto& c : s.fCases) {
1106 if (c->fValue) {
1107 this->write("case ");
1108 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1109 this->writeLine(":");
1110 } else {
1111 this->writeLine("default:");
1112 }
1113 fIndentation++;
1114 for (const auto& stmt : c->fStatements) {
1115 this->writeStatement(*stmt);
1116 this->writeLine();
1117 }
1118 fIndentation--;
1119 }
1120 fIndentation--;
1121 this->write("}");
1122}
1123
1124void MetalCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1125 this->write("return");
1126 if (r.fExpression) {
1127 this->write(" ");
1128 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1129 }
1130 this->write(";");
1131}
1132
1133void MetalCodeGenerator::writeHeader() {
1134 this->write("#include <metal_stdlib>\n");
1135 this->write("#include <simd/simd.h>\n");
1136 this->write("using namespace metal;\n");
1137}
1138
1139void MetalCodeGenerator::writeUniformStruct() {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001140 for (const auto& e : fProgram) {
1141 if (ProgramElement::kVar_Kind == e.fKind) {
1142 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001143 if (!decls.fVars.size()) {
1144 continue;
1145 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001146 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001147 if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1148 first.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001149 if (-1 == fUniformBuffer) {
1150 this->write("struct Uniforms {\n");
1151 fUniformBuffer = first.fModifiers.fLayout.fSet;
1152 if (-1 == fUniformBuffer) {
1153 fErrors.error(decls.fOffset, "Metal uniforms must have 'layout(set=...)'");
1154 }
1155 } else if (first.fModifiers.fLayout.fSet != fUniformBuffer) {
1156 if (-1 == fUniformBuffer) {
1157 fErrors.error(decls.fOffset, "Metal backend requires all uniforms to have "
1158 "the same 'layout(set=...)'");
1159 }
1160 }
1161 this->write(" ");
1162 this->writeType(first.fType);
1163 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001164 for (const auto& stmt : decls.fVars) {
1165 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001166 this->writeName(var.fVar->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -04001167 }
1168 this->write(";\n");
1169 }
1170 }
1171 }
1172 if (-1 != fUniformBuffer) {
1173 this->write("};\n");
1174 }
1175}
1176
1177void MetalCodeGenerator::writeInputStruct() {
1178 this->write("struct Inputs {\n");
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001179 for (const auto& e : fProgram) {
1180 if (ProgramElement::kVar_Kind == e.fKind) {
1181 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001182 if (!decls.fVars.size()) {
1183 continue;
1184 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001185 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001186 if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
1187 -1 == first.fModifiers.fLayout.fBuiltin) {
1188 this->write(" ");
1189 this->writeType(first.fType);
1190 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001191 for (const auto& stmt : decls.fVars) {
1192 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001193 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001194 if (-1 != var.fVar->fModifiers.fLayout.fLocation) {
Timothy Liang7d637782018-06-05 09:58:07 -04001195 if (fProgram.fKind == Program::kVertex_Kind) {
1196 this->write(" [[attribute(" +
1197 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1198 } else if (fProgram.fKind == Program::kFragment_Kind) {
1199 this->write(" [[user(locn" +
1200 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1201 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001202 }
1203 }
1204 this->write(";\n");
1205 }
1206 }
1207 }
1208 this->write("};\n");
1209}
1210
1211void MetalCodeGenerator::writeOutputStruct() {
1212 this->write("struct Outputs {\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001213 if (fProgram.fKind == Program::kVertex_Kind) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001214 this->write(" float4 sk_Position [[position]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001215 } else if (fProgram.fKind == Program::kFragment_Kind) {
Timothy Liangde0be802018-08-10 13:48:08 -04001216 this->write(" float4 sk_FragColor [[color(0)]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001217 }
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001218 for (const auto& e : fProgram) {
1219 if (ProgramElement::kVar_Kind == e.fKind) {
1220 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001221 if (!decls.fVars.size()) {
1222 continue;
1223 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001224 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001225 if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
1226 -1 == first.fModifiers.fLayout.fBuiltin) {
1227 this->write(" ");
1228 this->writeType(first.fType);
1229 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001230 for (const auto& stmt : decls.fVars) {
1231 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001232 this->writeName(var.fVar->fName);
Timothy Liang7d637782018-06-05 09:58:07 -04001233 if (fProgram.fKind == Program::kVertex_Kind) {
1234 this->write(" [[user(locn" +
1235 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1236 } else if (fProgram.fKind == Program::kFragment_Kind) {
1237 this->write(" [[color(" +
Timothy Liangde0be802018-08-10 13:48:08 -04001238 to_string(var.fVar->fModifiers.fLayout.fLocation) +")");
1239 int colorIndex = var.fVar->fModifiers.fLayout.fIndex;
1240 if (colorIndex) {
1241 this->write(", index(" + to_string(colorIndex) + ")");
1242 }
1243 this->write("]]");
Timothy Liang7d637782018-06-05 09:58:07 -04001244 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001245 }
1246 this->write(";\n");
1247 }
1248 }
Timothy Liang7d637782018-06-05 09:58:07 -04001249 }
1250 if (fProgram.fKind == Program::kVertex_Kind) {
1251 this->write(" float sk_PointSize;\n");
1252 }
1253 this->write("};\n");
1254}
1255
1256void MetalCodeGenerator::writeInterfaceBlocks() {
1257 bool wroteInterfaceBlock = false;
1258 for (const auto& e : fProgram) {
1259 if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
1260 this->writeInterfaceBlock((InterfaceBlock&) e);
1261 wroteInterfaceBlock = true;
1262 }
1263 }
1264 if (!wroteInterfaceBlock && (fProgram.fKind == Program::kFragment_Kind)) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001265 // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
1266 // in a different way altogether.
Timothy Liang7d637782018-06-05 09:58:07 -04001267 this->writeLine("struct sksl_synthetic_uniforms {");
1268 this->writeLine(" float u_skRTHeight;");
1269 this->writeLine("};");
1270 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001271}
1272
Timothy Liangee84fe12018-05-18 14:38:19 -04001273void MetalCodeGenerator::writeGlobalStruct() {
1274 bool wroteStructDecl = false;
Timothy Liang7d637782018-06-05 09:58:07 -04001275 for (const auto& intf : fInterfaceBlockNameMap) {
1276 if (!wroteStructDecl) {
1277 this->write("struct Globals {\n");
1278 wroteStructDecl = true;
1279 }
1280 fNeedsGlobalStructInit = true;
1281 const auto& intfType = intf.first;
1282 const auto& intfName = intf.second;
1283 this->write(" constant ");
1284 this->write(intfType->fTypeName);
1285 this->write("* ");
Timothy Liang651286f2018-06-07 09:55:33 -04001286 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -04001287 this->write(";\n");
1288 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001289 for (const auto& e : fProgram) {
1290 if (ProgramElement::kVar_Kind == e.fKind) {
1291 VarDeclarations& decls = (VarDeclarations&) e;
1292 if (!decls.fVars.size()) {
1293 continue;
1294 }
1295 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001296 if ((!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) ||
1297 first.fType.kind() == Type::kSampler_Kind) {
Timothy Liangee84fe12018-05-18 14:38:19 -04001298 if (!wroteStructDecl) {
1299 this->write("struct Globals {\n");
1300 wroteStructDecl = true;
1301 }
1302 fNeedsGlobalStructInit = true;
1303 this->write(" ");
1304 this->writeType(first.fType);
1305 this->write(" ");
1306 for (const auto& stmt : decls.fVars) {
1307 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001308 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001309 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
1310 fTextures.push_back(var.fVar);
1311 this->write(";\n");
1312 this->write(" sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -04001313 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001314 this->write(SAMPLER_SUFFIX);
1315 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001316 if (var.fValue) {
1317 fInitNonConstGlobalVars.push_back(&var);
1318 }
1319 }
1320 this->write(";\n");
1321 }
1322 }
1323 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001324 if (wroteStructDecl) {
1325 this->write("};\n");
1326 }
1327}
1328
Ethan Nicholascc305772017-10-13 16:17:45 -04001329void MetalCodeGenerator::writeProgramElement(const ProgramElement& e) {
1330 switch (e.fKind) {
1331 case ProgramElement::kExtension_Kind:
1332 break;
1333 case ProgramElement::kVar_Kind: {
1334 VarDeclarations& decl = (VarDeclarations&) e;
1335 if (decl.fVars.size() > 0) {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001336 int builtin = ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
Ethan Nicholascc305772017-10-13 16:17:45 -04001337 if (-1 == builtin) {
1338 // normal var
1339 this->writeVarDeclarations(decl, true);
1340 this->writeLine();
1341 } else if (SK_FRAGCOLOR_BUILTIN == builtin) {
1342 // ignore
1343 }
1344 }
1345 break;
1346 }
1347 case ProgramElement::kInterfaceBlock_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -04001348 // handled in writeInterfaceBlocks, do nothing
Ethan Nicholascc305772017-10-13 16:17:45 -04001349 break;
1350 case ProgramElement::kFunction_Kind:
1351 this->writeFunction((FunctionDefinition&) e);
1352 break;
1353 case ProgramElement::kModifiers_Kind:
1354 this->writeModifiers(((ModifiersDeclaration&) e).fModifiers, true);
1355 this->writeLine(";");
1356 break;
1357 default:
1358 printf("%s\n", e.description().c_str());
1359 ABORT("unsupported program element");
1360 }
1361}
1362
1363MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Expression& e) {
1364 switch (e.fKind) {
1365 case Expression::kFunctionCall_Kind: {
1366 const FunctionCall& f = (const FunctionCall&) e;
1367 Requirements result = this->requirements(f.fFunction);
1368 for (const auto& e : f.fArguments) {
1369 result |= this->requirements(*e);
1370 }
1371 return result;
1372 }
1373 case Expression::kConstructor_Kind: {
1374 const Constructor& c = (const Constructor&) e;
1375 Requirements result = kNo_Requirements;
1376 for (const auto& e : c.fArguments) {
1377 result |= this->requirements(*e);
1378 }
1379 return result;
1380 }
Timothy Liang7d637782018-06-05 09:58:07 -04001381 case Expression::kFieldAccess_Kind: {
1382 const FieldAccess& f = (const FieldAccess&) e;
1383 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
1384 return kGlobals_Requirement;
1385 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001386 return this->requirements(*((const FieldAccess&) e).fBase);
Timothy Liang7d637782018-06-05 09:58:07 -04001387 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001388 case Expression::kSwizzle_Kind:
1389 return this->requirements(*((const Swizzle&) e).fBase);
1390 case Expression::kBinary_Kind: {
1391 const BinaryExpression& b = (const BinaryExpression&) e;
1392 return this->requirements(*b.fLeft) | this->requirements(*b.fRight);
1393 }
1394 case Expression::kIndex_Kind: {
1395 const IndexExpression& idx = (const IndexExpression&) e;
1396 return this->requirements(*idx.fBase) | this->requirements(*idx.fIndex);
1397 }
1398 case Expression::kPrefix_Kind:
1399 return this->requirements(*((const PrefixExpression&) e).fOperand);
1400 case Expression::kPostfix_Kind:
1401 return this->requirements(*((const PostfixExpression&) e).fOperand);
1402 case Expression::kTernary_Kind: {
1403 const TernaryExpression& t = (const TernaryExpression&) e;
1404 return this->requirements(*t.fTest) | this->requirements(*t.fIfTrue) |
1405 this->requirements(*t.fIfFalse);
1406 }
1407 case Expression::kVariableReference_Kind: {
1408 const VariableReference& v = (const VariableReference&) e;
1409 Requirements result = kNo_Requirements;
1410 if (v.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
1411 result = kInputs_Requirement;
1412 } else if (Variable::kGlobal_Storage == v.fVariable.fStorage) {
1413 if (v.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
1414 result = kInputs_Requirement;
1415 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
1416 result = kOutputs_Requirement;
Timothy Lianga06f2152018-05-24 15:33:31 -04001417 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1418 v.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001419 result = kUniforms_Requirement;
Timothy Liangee84fe12018-05-18 14:38:19 -04001420 } else {
1421 result = kGlobals_Requirement;
Ethan Nicholascc305772017-10-13 16:17:45 -04001422 }
1423 }
1424 return result;
1425 }
1426 default:
1427 return kNo_Requirements;
1428 }
1429}
1430
1431MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Statement& s) {
1432 switch (s.fKind) {
1433 case Statement::kBlock_Kind: {
1434 Requirements result = kNo_Requirements;
1435 for (const auto& child : ((const Block&) s).fStatements) {
1436 result |= this->requirements(*child);
1437 }
1438 return result;
1439 }
Timothy Liang7d637782018-06-05 09:58:07 -04001440 case Statement::kVarDeclaration_Kind: {
1441 Requirements result = kNo_Requirements;
1442 const VarDeclaration& var = (const VarDeclaration&) s;
1443 if (var.fValue) {
1444 result = this->requirements(*var.fValue);
1445 }
1446 return result;
1447 }
1448 case Statement::kVarDeclarations_Kind: {
1449 Requirements result = kNo_Requirements;
1450 const VarDeclarations& decls = *((const VarDeclarationsStatement&) s).fDeclaration;
1451 for (const auto& stmt : decls.fVars) {
1452 result |= this->requirements(*stmt);
1453 }
1454 return result;
1455 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001456 case Statement::kExpression_Kind:
1457 return this->requirements(*((const ExpressionStatement&) s).fExpression);
1458 case Statement::kReturn_Kind: {
1459 const ReturnStatement& r = (const ReturnStatement&) s;
1460 if (r.fExpression) {
1461 return this->requirements(*r.fExpression);
1462 }
1463 return kNo_Requirements;
1464 }
1465 case Statement::kIf_Kind: {
1466 const IfStatement& i = (const IfStatement&) s;
1467 return this->requirements(*i.fTest) |
1468 this->requirements(*i.fIfTrue) |
1469 (i.fIfFalse && this->requirements(*i.fIfFalse));
1470 }
1471 case Statement::kFor_Kind: {
1472 const ForStatement& f = (const ForStatement&) s;
1473 return this->requirements(*f.fInitializer) |
1474 this->requirements(*f.fTest) |
1475 this->requirements(*f.fNext) |
1476 this->requirements(*f.fStatement);
1477 }
1478 case Statement::kWhile_Kind: {
1479 const WhileStatement& w = (const WhileStatement&) s;
1480 return this->requirements(*w.fTest) |
1481 this->requirements(*w.fStatement);
1482 }
1483 case Statement::kDo_Kind: {
1484 const DoStatement& d = (const DoStatement&) s;
1485 return this->requirements(*d.fTest) |
1486 this->requirements(*d.fStatement);
1487 }
1488 case Statement::kSwitch_Kind: {
1489 const SwitchStatement& sw = (const SwitchStatement&) s;
1490 Requirements result = this->requirements(*sw.fValue);
1491 for (const auto& c : sw.fCases) {
1492 for (const auto& st : c->fStatements) {
1493 result |= this->requirements(*st);
1494 }
1495 }
1496 return result;
1497 }
1498 default:
1499 return kNo_Requirements;
1500 }
1501}
1502
1503MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const FunctionDeclaration& f) {
1504 if (f.fBuiltin) {
1505 return kNo_Requirements;
1506 }
1507 auto found = fRequirements.find(&f);
1508 if (found == fRequirements.end()) {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001509 for (const auto& e : fProgram) {
1510 if (ProgramElement::kFunction_Kind == e.fKind) {
1511 const FunctionDefinition& def = (const FunctionDefinition&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001512 if (&def.fDeclaration == &f) {
1513 Requirements reqs = this->requirements(*def.fBody);
1514 fRequirements[&f] = reqs;
1515 return reqs;
1516 }
1517 }
1518 }
1519 }
1520 return found->second;
1521}
1522
Timothy Liangb8eeb802018-07-23 16:46:16 -04001523bool MetalCodeGenerator::generateCode() {
Ethan Nicholascc305772017-10-13 16:17:45 -04001524 OutputStream* rawOut = fOut;
1525 fOut = &fHeader;
Timothy Liangb8eeb802018-07-23 16:46:16 -04001526#ifdef SK_MOLTENVK
1527 fOut->write((const char*) &MVKMagicNum, sizeof(MVKMagicNum));
1528#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001529 fProgramKind = fProgram.fKind;
1530 this->writeHeader();
1531 this->writeUniformStruct();
1532 this->writeInputStruct();
Timothy Liang7d637782018-06-05 09:58:07 -04001533 this->writeOutputStruct();
1534 this->writeInterfaceBlocks();
Timothy Liangee84fe12018-05-18 14:38:19 -04001535 this->writeGlobalStruct();
Ethan Nicholascc305772017-10-13 16:17:45 -04001536 StringStream body;
1537 fOut = &body;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001538 for (const auto& e : fProgram) {
1539 this->writeProgramElement(e);
Ethan Nicholascc305772017-10-13 16:17:45 -04001540 }
1541 fOut = rawOut;
1542
1543 write_stringstream(fHeader, *rawOut);
Chris Daltondba7aab2018-11-15 10:57:49 -05001544 write_stringstream(fExtraFunctions, *rawOut);
Ethan Nicholascc305772017-10-13 16:17:45 -04001545 write_stringstream(body, *rawOut);
Timothy Liangb8eeb802018-07-23 16:46:16 -04001546#ifdef SK_MOLTENVK
1547 this->write("\0");
1548#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001549 return true;
Ethan Nicholascc305772017-10-13 16:17:45 -04001550}
1551
1552}