blob: 928fa2377f9723a36a9e14aff7c7bbc1149bd898 [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() {
Jim Van Verth6bc650e2019-02-07 14:53:23 -0500404 if (fProgram.fInputs.fRTHeight) {
405 this->write("float4(_fragCoord.x, _anonInterface0.u_skRTHeight - _fragCoord.y, 0.0, "
406 "_fragCoord.w)");
407 } else {
408 this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
409 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400410}
411
412void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
413 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
414 case SK_FRAGCOLOR_BUILTIN:
Timothy Liang7d637782018-06-05 09:58:07 -0400415 this->write("_out->sk_FragColor");
Ethan Nicholascc305772017-10-13 16:17:45 -0400416 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400417 case SK_FRAGCOORD_BUILTIN:
418 this->writeFragCoord();
419 break;
Timothy Liangdc89f192018-06-13 09:20:31 -0400420 case SK_VERTEXID_BUILTIN:
421 this->write("sk_VertexID");
422 break;
423 case SK_INSTANCEID_BUILTIN:
424 this->write("sk_InstanceID");
425 break;
Timothy Liang7b8875d2018-08-10 09:42:31 -0400426 case SK_CLOCKWISE_BUILTIN:
427 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
428 // clockwise to match Skia convention. This is also the default in MoltenVK.
429 this->write(fProgram.fSettings.fFlipY ? "_frontFacing" : "(!_frontFacing)");
430 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400431 default:
432 if (Variable::kGlobal_Storage == ref.fVariable.fStorage) {
433 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
434 this->write("_in.");
435 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400436 this->write("_out->");
Timothy Lianga06f2152018-05-24 15:33:31 -0400437 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
438 ref.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400439 this->write("_uniforms.");
440 } else {
Timothy Liangee84fe12018-05-18 14:38:19 -0400441 this->write("_globals->");
Ethan Nicholascc305772017-10-13 16:17:45 -0400442 }
443 }
Timothy Liang651286f2018-06-07 09:55:33 -0400444 this->writeName(ref.fVariable.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400445 }
446}
447
448void MetalCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
449 this->writeExpression(*expr.fBase, kPostfix_Precedence);
450 this->write("[");
451 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
452 this->write("]");
453}
454
455void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
Timothy Liang7d637782018-06-05 09:58:07 -0400456 const Type::Field* field = &f.fBase->fType.fields()[f.fFieldIndex];
Ethan Nicholascc305772017-10-13 16:17:45 -0400457 if (FieldAccess::kDefault_OwnerKind == f.fOwnerKind) {
458 this->writeExpression(*f.fBase, kPostfix_Precedence);
459 this->write(".");
460 }
Timothy Liang7d637782018-06-05 09:58:07 -0400461 switch (field->fModifiers.fLayout.fBuiltin) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400462 case SK_CLIPDISTANCE_BUILTIN:
463 this->write("gl_ClipDistance");
464 break;
465 case SK_POSITION_BUILTIN:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400466 this->write("_out->sk_Position");
Ethan Nicholascc305772017-10-13 16:17:45 -0400467 break;
468 default:
Timothy Liang7d637782018-06-05 09:58:07 -0400469 if (field->fName == "sk_PointSize") {
470 this->write("_out->sk_PointSize");
471 } else {
472 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
473 this->write("_globals->");
474 this->write(fInterfaceBlockNameMap[fInterfaceBlockMap[field]]);
475 this->write("->");
476 }
Timothy Liang651286f2018-06-07 09:55:33 -0400477 this->writeName(field->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400478 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400479 }
480}
481
482void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
483 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
484 this->write(".");
485 for (int c : swizzle.fComponents) {
486 this->write(&("x\0y\0z\0w\0"[c * 2]));
487 }
488}
489
490MetalCodeGenerator::Precedence MetalCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
491 switch (op) {
492 case Token::STAR: // fall through
493 case Token::SLASH: // fall through
494 case Token::PERCENT: return MetalCodeGenerator::kMultiplicative_Precedence;
495 case Token::PLUS: // fall through
496 case Token::MINUS: return MetalCodeGenerator::kAdditive_Precedence;
497 case Token::SHL: // fall through
498 case Token::SHR: return MetalCodeGenerator::kShift_Precedence;
499 case Token::LT: // fall through
500 case Token::GT: // fall through
501 case Token::LTEQ: // fall through
502 case Token::GTEQ: return MetalCodeGenerator::kRelational_Precedence;
503 case Token::EQEQ: // fall through
504 case Token::NEQ: return MetalCodeGenerator::kEquality_Precedence;
505 case Token::BITWISEAND: return MetalCodeGenerator::kBitwiseAnd_Precedence;
506 case Token::BITWISEXOR: return MetalCodeGenerator::kBitwiseXor_Precedence;
507 case Token::BITWISEOR: return MetalCodeGenerator::kBitwiseOr_Precedence;
508 case Token::LOGICALAND: return MetalCodeGenerator::kLogicalAnd_Precedence;
509 case Token::LOGICALXOR: return MetalCodeGenerator::kLogicalXor_Precedence;
510 case Token::LOGICALOR: return MetalCodeGenerator::kLogicalOr_Precedence;
511 case Token::EQ: // fall through
512 case Token::PLUSEQ: // fall through
513 case Token::MINUSEQ: // fall through
514 case Token::STAREQ: // fall through
515 case Token::SLASHEQ: // fall through
516 case Token::PERCENTEQ: // fall through
517 case Token::SHLEQ: // fall through
518 case Token::SHREQ: // fall through
519 case Token::LOGICALANDEQ: // fall through
520 case Token::LOGICALXOREQ: // fall through
521 case Token::LOGICALOREQ: // fall through
522 case Token::BITWISEANDEQ: // fall through
523 case Token::BITWISEXOREQ: // fall through
524 case Token::BITWISEOREQ: return MetalCodeGenerator::kAssignment_Precedence;
525 case Token::COMMA: return MetalCodeGenerator::kSequence_Precedence;
526 default: ABORT("unsupported binary operator");
527 }
528}
529
530void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
531 Precedence parentPrecedence) {
532 Precedence precedence = GetBinaryPrecedence(b.fOperator);
533 if (precedence >= parentPrecedence) {
534 this->write("(");
535 }
536 if (Compiler::IsAssignment(b.fOperator) &&
537 Expression::kVariableReference_Kind == b.fLeft->fKind &&
538 Variable::kParameter_Storage == ((VariableReference&) *b.fLeft).fVariable.fStorage &&
539 (((VariableReference&) *b.fLeft).fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) {
540 // writing to an out parameter. Since we have to turn those into pointers, we have to
541 // dereference it here.
542 this->write("*");
543 }
544 this->writeExpression(*b.fLeft, precedence);
545 if (b.fOperator != Token::EQ && Compiler::IsAssignment(b.fOperator) &&
546 Expression::kSwizzle_Kind == b.fLeft->fKind && !b.fLeft->hasSideEffects()) {
547 // This doesn't compile in Metal:
548 // float4 x = float4(1);
549 // x.xy *= float2x2(...);
550 // with the error message "non-const reference cannot bind to vector element",
551 // but switching it to x.xy = x.xy * float2x2(...) fixes it. We perform this tranformation
552 // as long as the LHS has no side effects, and hope for the best otherwise.
553 this->write(" = ");
554 this->writeExpression(*b.fLeft, kAssignment_Precedence);
555 this->write(" ");
556 String op = Compiler::OperatorName(b.fOperator);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400557 SkASSERT(op.endsWith("="));
Ethan Nicholascc305772017-10-13 16:17:45 -0400558 this->write(op.substr(0, op.size() - 1).c_str());
559 this->write(" ");
560 } else {
561 this->write(String(" ") + Compiler::OperatorName(b.fOperator) + " ");
562 }
563 this->writeExpression(*b.fRight, precedence);
564 if (precedence >= parentPrecedence) {
565 this->write(")");
566 }
567}
568
569void MetalCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
570 Precedence parentPrecedence) {
571 if (kTernary_Precedence >= parentPrecedence) {
572 this->write("(");
573 }
574 this->writeExpression(*t.fTest, kTernary_Precedence);
575 this->write(" ? ");
576 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
577 this->write(" : ");
578 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
579 if (kTernary_Precedence >= parentPrecedence) {
580 this->write(")");
581 }
582}
583
584void MetalCodeGenerator::writePrefixExpression(const PrefixExpression& p,
585 Precedence parentPrecedence) {
586 if (kPrefix_Precedence >= parentPrecedence) {
587 this->write("(");
588 }
589 this->write(Compiler::OperatorName(p.fOperator));
590 this->writeExpression(*p.fOperand, kPrefix_Precedence);
591 if (kPrefix_Precedence >= parentPrecedence) {
592 this->write(")");
593 }
594}
595
596void MetalCodeGenerator::writePostfixExpression(const PostfixExpression& p,
597 Precedence parentPrecedence) {
598 if (kPostfix_Precedence >= parentPrecedence) {
599 this->write("(");
600 }
601 this->writeExpression(*p.fOperand, kPostfix_Precedence);
602 this->write(Compiler::OperatorName(p.fOperator));
603 if (kPostfix_Precedence >= parentPrecedence) {
604 this->write(")");
605 }
606}
607
608void MetalCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
609 this->write(b.fValue ? "true" : "false");
610}
611
612void MetalCodeGenerator::writeIntLiteral(const IntLiteral& i) {
613 if (i.fType == *fContext.fUInt_Type) {
614 this->write(to_string(i.fValue & 0xffffffff) + "u");
615 } else {
616 this->write(to_string((int32_t) i.fValue));
617 }
618}
619
620void MetalCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
621 this->write(to_string(f.fValue));
622}
623
624void MetalCodeGenerator::writeSetting(const Setting& s) {
625 ABORT("internal error; setting was not folded to a constant during compilation\n");
626}
627
628void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
629 const char* separator = "";
630 if ("main" == f.fDeclaration.fName) {
631 switch (fProgram.fKind) {
632 case Program::kFragment_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400633#ifdef SK_MOLTENVK
634 this->write("fragment Outputs main0");
635#else
636 this->write("fragment Outputs fragmentMain");
637#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400638 break;
639 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400640#ifdef SK_MOLTENVK
Timothy Lianga06f2152018-05-24 15:33:31 -0400641 this->write("vertex Outputs main0");
Timothy Liangb8eeb802018-07-23 16:46:16 -0400642#else
643 this->write("vertex Outputs vertexMain");
644#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400645 break;
646 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400647 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400648 }
649 this->write("(Inputs _in [[stage_in]]");
650 if (-1 != fUniformBuffer) {
651 this->write(", constant Uniforms& _uniforms [[buffer(" +
652 to_string(fUniformBuffer) + ")]]");
653 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400654 for (const auto& e : fProgram) {
655 if (ProgramElement::kVar_Kind == e.fKind) {
656 VarDeclarations& decls = (VarDeclarations&) e;
657 if (!decls.fVars.size()) {
658 continue;
659 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400660 for (const auto& stmt: decls.fVars) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400661 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400662 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
Timothy Liang7d637782018-06-05 09:58:07 -0400663 this->write(", texture2d<float> "); // FIXME - support other texture types
Timothy Liang651286f2018-06-07 09:55:33 -0400664 this->writeName(var.fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400665 this->write("[[texture(");
Timothy Lianga06f2152018-05-24 15:33:31 -0400666 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
667 this->write(")]]");
668 this->write(", sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -0400669 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400670 this->write(SAMPLER_SUFFIX);
671 this->write("[[sampler(");
672 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
Timothy Liangee84fe12018-05-18 14:38:19 -0400673 this->write(")]]");
674 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400675 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400676 } else if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
677 InterfaceBlock& intf = (InterfaceBlock&) e;
678 if ("sk_PerVertex" == intf.fTypeName) {
679 continue;
680 }
681 this->write(", constant ");
682 this->writeType(intf.fVariable.fType);
683 this->write("& " );
684 this->write(fInterfaceBlockNameMap[&intf]);
685 this->write(" [[buffer(");
Timothy Liang057c3902018-08-08 10:48:45 -0400686#ifdef SK_MOLTENVK
Timothy Liang7d637782018-06-05 09:58:07 -0400687 this->write(to_string(intf.fVariable.fModifiers.fLayout.fSet));
Timothy Liang057c3902018-08-08 10:48:45 -0400688#else
689 this->write(to_string(intf.fVariable.fModifiers.fLayout.fBinding));
690#endif
Timothy Lianga06f2152018-05-24 15:33:31 -0400691 this->write(")]]");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400692 }
693 }
Jim Van Verth6bc650e2019-02-07 14:53:23 -0500694 if (fProgram.fKind == Program::kFragment_Kind) {
695 if (fProgram.fInputs.fRTHeight && fInterfaceBlockNameMap.empty()) {
Timothy Liang5422f9a2018-08-10 10:57:55 -0400696#ifdef SK_MOLTENVK
697 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(0)]]");
698#else
699 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
700#endif
701 }
Timothy Liang7b8875d2018-08-10 09:42:31 -0400702 this->write(", bool _frontFacing [[front_facing]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400703 this->write(", float4 _fragCoord [[position]]");
Timothy Liangdc89f192018-06-13 09:20:31 -0400704 } else if (fProgram.fKind == Program::kVertex_Kind) {
705 this->write(", uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400706 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400707 separator = ", ";
708 } else {
709 this->writeType(f.fDeclaration.fReturnType);
Timothy Liang651286f2018-06-07 09:55:33 -0400710 this->write(" ");
711 this->writeName(f.fDeclaration.fName);
712 this->write("(");
Ethan Nicholascc305772017-10-13 16:17:45 -0400713 if (this->requirements(f.fDeclaration) & kInputs_Requirement) {
714 this->write("Inputs _in");
715 separator = ", ";
716 }
717 if (this->requirements(f.fDeclaration) & kOutputs_Requirement) {
718 this->write(separator);
Timothy Liangee84fe12018-05-18 14:38:19 -0400719 this->write("thread Outputs* _out");
Ethan Nicholascc305772017-10-13 16:17:45 -0400720 separator = ", ";
721 }
722 if (this->requirements(f.fDeclaration) & kUniforms_Requirement) {
723 this->write(separator);
724 this->write("Uniforms _uniforms");
725 separator = ", ";
726 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400727 if (this->requirements(f.fDeclaration) & kGlobals_Requirement) {
728 this->write(separator);
729 this->write("thread Globals* _globals");
730 separator = ", ";
731 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400732 }
733 for (const auto& param : f.fDeclaration.fParameters) {
734 this->write(separator);
735 separator = ", ";
736 this->writeModifiers(param->fModifiers, false);
737 std::vector<int> sizes;
738 const Type* type = &param->fType;
739 while (Type::kArray_Kind == type->kind()) {
740 sizes.push_back(type->columns());
741 type = &type->componentType();
742 }
743 this->writeType(*type);
744 if (param->fModifiers.fFlags & Modifiers::kOut_Flag) {
745 this->write("*");
746 }
Timothy Liang651286f2018-06-07 09:55:33 -0400747 this->write(" ");
748 this->writeName(param->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400749 for (int s : sizes) {
750 if (s <= 0) {
751 this->write("[]");
752 } else {
753 this->write("[" + to_string(s) + "]");
754 }
755 }
756 }
757 this->writeLine(") {");
758
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400759 SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
Brian Salomondc092132018-04-04 10:14:16 -0400760
Ethan Nicholascc305772017-10-13 16:17:45 -0400761 if ("main" == f.fDeclaration.fName) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400762 if (fNeedsGlobalStructInit) {
763 this->writeLine(" Globals globalStruct;");
764 this->writeLine(" thread Globals* _globals = &globalStruct;");
Timothy Liang7d637782018-06-05 09:58:07 -0400765 for (const auto& intf: fInterfaceBlockNameMap) {
766 const auto& intfName = intf.second;
767 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400768 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400769 this->write(" = &");
Timothy Liang651286f2018-06-07 09:55:33 -0400770 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400771 this->write(";\n");
772 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400773 for (const auto& var: fInitNonConstGlobalVars) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400774 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400775 this->writeName(var->fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400776 this->write(" = ");
777 this->writeVarInitializer(*var->fVar, *var->fValue);
778 this->writeLine(";");
779 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400780 for (const auto& texture: fTextures) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400781 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400782 this->writeName(texture->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400783 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400784 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400785 this->write(";\n");
786 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400787 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400788 this->write(SAMPLER_SUFFIX);
789 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400790 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400791 this->write(SAMPLER_SUFFIX);
Timothy Liangee84fe12018-05-18 14:38:19 -0400792 this->write(";\n");
793 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400794 }
Timothy Liang7d637782018-06-05 09:58:07 -0400795 this->writeLine(" Outputs _outputStruct;");
796 this->writeLine(" thread Outputs* _out = &_outputStruct;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400797 }
798 fFunctionHeader = "";
799 OutputStream* oldOut = fOut;
800 StringStream buffer;
801 fOut = &buffer;
802 fIndentation++;
803 this->writeStatements(((Block&) *f.fBody).fStatements);
804 if ("main" == f.fDeclaration.fName) {
805 switch (fProgram.fKind) {
806 case Program::kFragment_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -0400807 this->writeLine("return *_out;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400808 break;
809 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400810 this->writeLine("_out->sk_Position.y = -_out->sk_Position.y;");
Timothy Lianga06f2152018-05-24 15:33:31 -0400811 this->writeLine("return *_out;"); // FIXME - detect if function already has return
Ethan Nicholascc305772017-10-13 16:17:45 -0400812 break;
813 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400814 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400815 }
816 }
817 fIndentation--;
818 this->writeLine("}");
819
820 fOut = oldOut;
821 this->write(fFunctionHeader);
822 this->write(buffer.str());
823}
824
825void MetalCodeGenerator::writeModifiers(const Modifiers& modifiers,
826 bool globalContext) {
827 if (modifiers.fFlags & Modifiers::kOut_Flag) {
828 this->write("thread ");
829 }
830 if (modifiers.fFlags & Modifiers::kConst_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400831 this->write("constant ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400832 }
833}
834
835void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
836 if ("sk_PerVertex" == intf.fTypeName) {
837 return;
838 }
839 this->writeModifiers(intf.fVariable.fModifiers, true);
Timothy Liangdc89f192018-06-13 09:20:31 -0400840 this->write("struct ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400841 this->writeLine(intf.fTypeName + " {");
Ethan Nicholascc305772017-10-13 16:17:45 -0400842 const Type* structType = &intf.fVariable.fType;
Timothy Lianga06f2152018-05-24 15:33:31 -0400843 fWrittenStructs.push_back(structType);
Ethan Nicholascc305772017-10-13 16:17:45 -0400844 while (Type::kArray_Kind == structType->kind()) {
845 structType = &structType->componentType();
846 }
Timothy Liangdc89f192018-06-13 09:20:31 -0400847 fIndentation++;
848 writeFields(structType->fields(), structType->fOffset, &intf);
Jim Van Verth3d482992019-02-07 10:48:05 -0500849 if (fProgram.fInputs.fRTHeight) {
Timothy Liang7d637782018-06-05 09:58:07 -0400850 this->writeLine("float u_skRTHeight;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400851 }
852 fIndentation--;
853 this->write("}");
854 if (intf.fInstanceName.size()) {
855 this->write(" ");
856 this->write(intf.fInstanceName);
857 for (const auto& size : intf.fSizes) {
858 this->write("[");
859 if (size) {
860 this->writeExpression(*size, kTopLevel_Precedence);
861 }
862 this->write("]");
863 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400864 fInterfaceBlockNameMap[&intf] = intf.fInstanceName;
865 } else {
Timothy Liang7d637782018-06-05 09:58:07 -0400866 fInterfaceBlockNameMap[&intf] = "_anonInterface" + to_string(fAnonInterfaceCount++);
Ethan Nicholascc305772017-10-13 16:17:45 -0400867 }
868 this->writeLine(";");
869}
870
Timothy Liangdc89f192018-06-13 09:20:31 -0400871void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int parentOffset,
872 const InterfaceBlock* parentIntf) {
Timothy Liang609fbe32018-08-10 16:40:49 -0400873#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -0400874 MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
Timothy Liang609fbe32018-08-10 16:40:49 -0400875#else
876 MemoryLayout memoryLayout(MemoryLayout::kMetal_Standard);
877#endif
Timothy Liangdc89f192018-06-13 09:20:31 -0400878 int currentOffset = 0;
879 for (const auto& field: fields) {
880 int fieldOffset = field.fModifiers.fLayout.fOffset;
881 const Type* fieldType = field.fType;
882 if (fieldOffset != -1) {
883 if (currentOffset > fieldOffset) {
884 fErrors.error(parentOffset,
885 "offset of field '" + field.fName + "' must be at least " +
886 to_string((int) currentOffset));
887 } else if (currentOffset < fieldOffset) {
888 this->write("char pad");
889 this->write(to_string(fPaddingCount++));
890 this->write("[");
891 this->write(to_string(fieldOffset - currentOffset));
892 this->writeLine("];");
893 currentOffset = fieldOffset;
894 }
895 int alignment = memoryLayout.alignment(*fieldType);
896 if (fieldOffset % alignment) {
897 fErrors.error(parentOffset,
898 "offset of field '" + field.fName + "' must be a multiple of " +
899 to_string((int) alignment));
900 }
901 }
Timothy Liang609fbe32018-08-10 16:40:49 -0400902#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -0400903 if (fieldType->kind() == Type::kVector_Kind &&
904 fieldType->columns() == 3) {
Timothy Liang609fbe32018-08-10 16:40:49 -0400905 SkASSERT(memoryLayout.size(*fieldType) == 3);
Timothy Liangdc89f192018-06-13 09:20:31 -0400906 // Pack all vec3 types so that their size in bytes will match what was expected in the
907 // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
908 // has vec3 equal to 3 * component type.
Timothy Liang609fbe32018-08-10 16:40:49 -0400909
910 // FIXME - Packed vectors can't be accessed by swizzles, but can be indexed into. A
911 // combination of this being a problem which only occurs when using MoltenVK and the
912 // fact that we haven't swizzled a vec3 yet means that this problem hasn't been
913 // addressed.
Timothy Liangdc89f192018-06-13 09:20:31 -0400914 this->write(PACKED_PREFIX);
915 }
Timothy Liang609fbe32018-08-10 16:40:49 -0400916#endif
Timothy Liangdc89f192018-06-13 09:20:31 -0400917 currentOffset += memoryLayout.size(*fieldType);
918 std::vector<int> sizes;
919 while (fieldType->kind() == Type::kArray_Kind) {
920 sizes.push_back(fieldType->columns());
921 fieldType = &fieldType->componentType();
922 }
923 this->writeModifiers(field.fModifiers, false);
924 this->writeType(*fieldType);
925 this->write(" ");
926 this->writeName(field.fName);
927 for (int s : sizes) {
928 if (s <= 0) {
929 this->write("[]");
930 } else {
931 this->write("[" + to_string(s) + "]");
932 }
933 }
934 this->writeLine(";");
935 if (parentIntf) {
936 fInterfaceBlockMap[&field] = parentIntf;
937 }
938 }
939}
940
Ethan Nicholascc305772017-10-13 16:17:45 -0400941void MetalCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
942 this->writeExpression(value, kTopLevel_Precedence);
943}
944
Timothy Liang651286f2018-06-07 09:55:33 -0400945void MetalCodeGenerator::writeName(const String& name) {
946 if (fReservedWords.find(name) != fReservedWords.end()) {
947 this->write("_"); // adding underscore before name to avoid conflict with reserved words
948 }
949 this->write(name);
950}
951
Ethan Nicholascc305772017-10-13 16:17:45 -0400952void MetalCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400953 SkASSERT(decl.fVars.size() > 0);
Ethan Nicholascc305772017-10-13 16:17:45 -0400954 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000955 for (const auto& stmt : decl.fVars) {
956 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400957 if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400958 continue;
959 }
960 if (wroteType) {
961 this->write(", ");
962 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000963 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholascc305772017-10-13 16:17:45 -0400964 this->writeType(decl.fBaseType);
965 this->write(" ");
966 wroteType = true;
967 }
Timothy Liang651286f2018-06-07 09:55:33 -0400968 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000969 for (const auto& size : var.fSizes) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400970 this->write("[");
971 if (size) {
972 this->writeExpression(*size, kTopLevel_Precedence);
973 }
974 this->write("]");
975 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000976 if (var.fValue) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400977 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000978 this->writeVarInitializer(*var.fVar, *var.fValue);
Ethan Nicholascc305772017-10-13 16:17:45 -0400979 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000980 if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400981 if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
982 fHeader.writeText("#extension ");
983 fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
984 fHeader.writeText(" : require\n");
985 }
986 fFoundImageDecl = true;
987 }
988 }
989 if (wroteType) {
990 this->write(";");
991 }
992}
993
994void MetalCodeGenerator::writeStatement(const Statement& s) {
995 switch (s.fKind) {
996 case Statement::kBlock_Kind:
997 this->writeBlock((Block&) s);
998 break;
999 case Statement::kExpression_Kind:
1000 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
1001 this->write(";");
1002 break;
1003 case Statement::kReturn_Kind:
1004 this->writeReturnStatement((ReturnStatement&) s);
1005 break;
1006 case Statement::kVarDeclarations_Kind:
1007 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
1008 break;
1009 case Statement::kIf_Kind:
1010 this->writeIfStatement((IfStatement&) s);
1011 break;
1012 case Statement::kFor_Kind:
1013 this->writeForStatement((ForStatement&) s);
1014 break;
1015 case Statement::kWhile_Kind:
1016 this->writeWhileStatement((WhileStatement&) s);
1017 break;
1018 case Statement::kDo_Kind:
1019 this->writeDoStatement((DoStatement&) s);
1020 break;
1021 case Statement::kSwitch_Kind:
1022 this->writeSwitchStatement((SwitchStatement&) s);
1023 break;
1024 case Statement::kBreak_Kind:
1025 this->write("break;");
1026 break;
1027 case Statement::kContinue_Kind:
1028 this->write("continue;");
1029 break;
1030 case Statement::kDiscard_Kind:
Timothy Lianga06f2152018-05-24 15:33:31 -04001031 this->write("discard_fragment();");
Ethan Nicholascc305772017-10-13 16:17:45 -04001032 break;
1033 case Statement::kNop_Kind:
1034 this->write(";");
1035 break;
1036 default:
1037 ABORT("unsupported statement: %s", s.description().c_str());
1038 }
1039}
1040
1041void MetalCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
1042 for (const auto& s : statements) {
1043 if (!s->isEmpty()) {
1044 this->writeStatement(*s);
1045 this->writeLine();
1046 }
1047 }
1048}
1049
1050void MetalCodeGenerator::writeBlock(const Block& b) {
1051 this->writeLine("{");
1052 fIndentation++;
1053 this->writeStatements(b.fStatements);
1054 fIndentation--;
1055 this->write("}");
1056}
1057
1058void MetalCodeGenerator::writeIfStatement(const IfStatement& stmt) {
1059 this->write("if (");
1060 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
1061 this->write(") ");
1062 this->writeStatement(*stmt.fIfTrue);
1063 if (stmt.fIfFalse) {
1064 this->write(" else ");
1065 this->writeStatement(*stmt.fIfFalse);
1066 }
1067}
1068
1069void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
1070 this->write("for (");
1071 if (f.fInitializer && !f.fInitializer->isEmpty()) {
1072 this->writeStatement(*f.fInitializer);
1073 } else {
1074 this->write("; ");
1075 }
1076 if (f.fTest) {
1077 this->writeExpression(*f.fTest, kTopLevel_Precedence);
1078 }
1079 this->write("; ");
1080 if (f.fNext) {
1081 this->writeExpression(*f.fNext, kTopLevel_Precedence);
1082 }
1083 this->write(") ");
1084 this->writeStatement(*f.fStatement);
1085}
1086
1087void MetalCodeGenerator::writeWhileStatement(const WhileStatement& w) {
1088 this->write("while (");
1089 this->writeExpression(*w.fTest, kTopLevel_Precedence);
1090 this->write(") ");
1091 this->writeStatement(*w.fStatement);
1092}
1093
1094void MetalCodeGenerator::writeDoStatement(const DoStatement& d) {
1095 this->write("do ");
1096 this->writeStatement(*d.fStatement);
1097 this->write(" while (");
1098 this->writeExpression(*d.fTest, kTopLevel_Precedence);
1099 this->write(");");
1100}
1101
1102void MetalCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
1103 this->write("switch (");
1104 this->writeExpression(*s.fValue, kTopLevel_Precedence);
1105 this->writeLine(") {");
1106 fIndentation++;
1107 for (const auto& c : s.fCases) {
1108 if (c->fValue) {
1109 this->write("case ");
1110 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1111 this->writeLine(":");
1112 } else {
1113 this->writeLine("default:");
1114 }
1115 fIndentation++;
1116 for (const auto& stmt : c->fStatements) {
1117 this->writeStatement(*stmt);
1118 this->writeLine();
1119 }
1120 fIndentation--;
1121 }
1122 fIndentation--;
1123 this->write("}");
1124}
1125
1126void MetalCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1127 this->write("return");
1128 if (r.fExpression) {
1129 this->write(" ");
1130 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1131 }
1132 this->write(";");
1133}
1134
1135void MetalCodeGenerator::writeHeader() {
1136 this->write("#include <metal_stdlib>\n");
1137 this->write("#include <simd/simd.h>\n");
1138 this->write("using namespace metal;\n");
1139}
1140
1141void MetalCodeGenerator::writeUniformStruct() {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001142 for (const auto& e : fProgram) {
1143 if (ProgramElement::kVar_Kind == e.fKind) {
1144 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001145 if (!decls.fVars.size()) {
1146 continue;
1147 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001148 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001149 if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1150 first.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001151 if (-1 == fUniformBuffer) {
1152 this->write("struct Uniforms {\n");
1153 fUniformBuffer = first.fModifiers.fLayout.fSet;
1154 if (-1 == fUniformBuffer) {
1155 fErrors.error(decls.fOffset, "Metal uniforms must have 'layout(set=...)'");
1156 }
1157 } else if (first.fModifiers.fLayout.fSet != fUniformBuffer) {
1158 if (-1 == fUniformBuffer) {
1159 fErrors.error(decls.fOffset, "Metal backend requires all uniforms to have "
1160 "the same 'layout(set=...)'");
1161 }
1162 }
1163 this->write(" ");
1164 this->writeType(first.fType);
1165 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001166 for (const auto& stmt : decls.fVars) {
1167 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001168 this->writeName(var.fVar->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -04001169 }
1170 this->write(";\n");
1171 }
1172 }
1173 }
1174 if (-1 != fUniformBuffer) {
1175 this->write("};\n");
1176 }
1177}
1178
1179void MetalCodeGenerator::writeInputStruct() {
1180 this->write("struct Inputs {\n");
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001181 for (const auto& e : fProgram) {
1182 if (ProgramElement::kVar_Kind == e.fKind) {
1183 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001184 if (!decls.fVars.size()) {
1185 continue;
1186 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001187 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001188 if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
1189 -1 == first.fModifiers.fLayout.fBuiltin) {
1190 this->write(" ");
1191 this->writeType(first.fType);
1192 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001193 for (const auto& stmt : decls.fVars) {
1194 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001195 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001196 if (-1 != var.fVar->fModifiers.fLayout.fLocation) {
Timothy Liang7d637782018-06-05 09:58:07 -04001197 if (fProgram.fKind == Program::kVertex_Kind) {
1198 this->write(" [[attribute(" +
1199 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1200 } else if (fProgram.fKind == Program::kFragment_Kind) {
1201 this->write(" [[user(locn" +
1202 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1203 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001204 }
1205 }
1206 this->write(";\n");
1207 }
1208 }
1209 }
1210 this->write("};\n");
1211}
1212
1213void MetalCodeGenerator::writeOutputStruct() {
1214 this->write("struct Outputs {\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001215 if (fProgram.fKind == Program::kVertex_Kind) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001216 this->write(" float4 sk_Position [[position]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001217 } else if (fProgram.fKind == Program::kFragment_Kind) {
Timothy Liangde0be802018-08-10 13:48:08 -04001218 this->write(" float4 sk_FragColor [[color(0)]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001219 }
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001220 for (const auto& e : fProgram) {
1221 if (ProgramElement::kVar_Kind == e.fKind) {
1222 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001223 if (!decls.fVars.size()) {
1224 continue;
1225 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001226 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001227 if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
1228 -1 == first.fModifiers.fLayout.fBuiltin) {
1229 this->write(" ");
1230 this->writeType(first.fType);
1231 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001232 for (const auto& stmt : decls.fVars) {
1233 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001234 this->writeName(var.fVar->fName);
Timothy Liang7d637782018-06-05 09:58:07 -04001235 if (fProgram.fKind == Program::kVertex_Kind) {
1236 this->write(" [[user(locn" +
1237 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1238 } else if (fProgram.fKind == Program::kFragment_Kind) {
1239 this->write(" [[color(" +
Timothy Liangde0be802018-08-10 13:48:08 -04001240 to_string(var.fVar->fModifiers.fLayout.fLocation) +")");
1241 int colorIndex = var.fVar->fModifiers.fLayout.fIndex;
1242 if (colorIndex) {
1243 this->write(", index(" + to_string(colorIndex) + ")");
1244 }
1245 this->write("]]");
Timothy Liang7d637782018-06-05 09:58:07 -04001246 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001247 }
1248 this->write(";\n");
1249 }
1250 }
Timothy Liang7d637782018-06-05 09:58:07 -04001251 }
1252 if (fProgram.fKind == Program::kVertex_Kind) {
1253 this->write(" float sk_PointSize;\n");
1254 }
1255 this->write("};\n");
1256}
1257
1258void MetalCodeGenerator::writeInterfaceBlocks() {
1259 bool wroteInterfaceBlock = false;
1260 for (const auto& e : fProgram) {
1261 if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
1262 this->writeInterfaceBlock((InterfaceBlock&) e);
1263 wroteInterfaceBlock = true;
1264 }
1265 }
Jim Van Verth3d482992019-02-07 10:48:05 -05001266 if (!wroteInterfaceBlock && fProgram.fInputs.fRTHeight) {
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}