blob: 3da4ec5b66004b9e4577d054f4a926030738b481 [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:
123 this->writeConstructor((Constructor&) expr);
124 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");
Timothy Liang7d637782018-06-05 09:58:07 -0400207 } else if (c.fFunction.fBuiltin && "dFdx" == c.fFunction.fName) {
208 this->write("dfdx");
209 } else if (c.fFunction.fBuiltin && "dFdy" == c.fFunction.fName) {
210 this->write("dfdy");
Ethan Nicholascc305772017-10-13 16:17:45 -0400211 } else {
Timothy Liang651286f2018-06-07 09:55:33 -0400212 this->writeName(c.fFunction.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400213 }
214 this->write("(");
215 const char* separator = "";
216 if (this->requirements(c.fFunction) & kInputs_Requirement) {
217 this->write("_in");
218 separator = ", ";
219 }
220 if (this->requirements(c.fFunction) & kOutputs_Requirement) {
221 this->write(separator);
222 this->write("_out");
223 separator = ", ";
224 }
225 if (this->requirements(c.fFunction) & kUniforms_Requirement) {
226 this->write(separator);
227 this->write("_uniforms");
228 separator = ", ";
229 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400230 if (this->requirements(c.fFunction) & kGlobals_Requirement) {
231 this->write(separator);
232 this->write("_globals");
233 separator = ", ";
234 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400235 for (size_t i = 0; i < c.fArguments.size(); ++i) {
236 const Expression& arg = *c.fArguments[i];
237 this->write(separator);
238 separator = ", ";
239 if (c.fFunction.fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag) {
240 this->write("&");
241 }
242 this->writeExpression(arg, kSequence_Precedence);
243 }
244 this->write(")");
245}
246
Timothy Liang6403b0e2018-05-17 10:40:04 -0400247void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
248 switch (kind) {
249 case kTexture_SpecialIntrinsic:
Timothy Liangee84fe12018-05-18 14:38:19 -0400250 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
Timothy Lianga06f2152018-05-24 15:33:31 -0400251 this->write(".sample(");
252 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
253 this->write(SAMPLER_SUFFIX);
254 this->write(", ");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400255 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
Timothy Liangee84fe12018-05-18 14:38:19 -0400256 if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
257 this->write(".xy)"); // FIXME - add projection functionality
258 } else {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400259 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
Timothy Liangee84fe12018-05-18 14:38:19 -0400260 this->write(")");
261 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400262 break;
Timothy Liang651286f2018-06-07 09:55:33 -0400263 case kMod_SpecialIntrinsic:
264 // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
265 this->write("((");
266 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
267 this->write(") - (");
268 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
269 this->write(") * floor((");
270 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
271 this->write(") / (");
272 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
273 this->write(")))");
274 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400275 default:
276 ABORT("unsupported special intrinsic kind");
277 }
278}
279
Ethan Nicholascc305772017-10-13 16:17:45 -0400280void MetalCodeGenerator::writeConstructor(const Constructor& c) {
281 this->writeType(c.fType);
282 this->write("(");
283 const char* separator = "";
284 int scalarCount = 0;
285 for (const auto& arg : c.fArguments) {
286 this->write(separator);
287 separator = ", ";
288 if (Type::kMatrix_Kind == c.fType.kind() && Type::kScalar_Kind == arg->fType.kind()) {
289 // float2x2(float, float, float, float) doesn't work in Metal 1, so we need to merge to
290 // float2x2(float2, float2).
291 if (!scalarCount) {
292 this->writeType(c.fType.componentType());
293 this->write(to_string(c.fType.rows()));
294 this->write("(");
295 }
296 ++scalarCount;
297 }
298 this->writeExpression(*arg, kSequence_Precedence);
299 if (scalarCount && scalarCount == c.fType.rows()) {
300 this->write(")");
301 scalarCount = 0;
302 }
303 }
304 this->write(")");
305}
306
307void MetalCodeGenerator::writeFragCoord() {
Timothy Liang7d637782018-06-05 09:58:07 -0400308 this->write("float4(_fragCoord.x, _anonInterface0.u_skRTHeight - _fragCoord.y, 0.0, 1.0)");
Ethan Nicholascc305772017-10-13 16:17:45 -0400309}
310
311void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
312 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
313 case SK_FRAGCOLOR_BUILTIN:
Timothy Liang7d637782018-06-05 09:58:07 -0400314 this->write("_out->sk_FragColor");
Ethan Nicholascc305772017-10-13 16:17:45 -0400315 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400316 case SK_FRAGCOORD_BUILTIN:
317 this->writeFragCoord();
318 break;
Timothy Liangdc89f192018-06-13 09:20:31 -0400319 case SK_VERTEXID_BUILTIN:
320 this->write("sk_VertexID");
321 break;
322 case SK_INSTANCEID_BUILTIN:
323 this->write("sk_InstanceID");
324 break;
Timothy Liang7b8875d2018-08-10 09:42:31 -0400325 case SK_CLOCKWISE_BUILTIN:
326 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
327 // clockwise to match Skia convention. This is also the default in MoltenVK.
328 this->write(fProgram.fSettings.fFlipY ? "_frontFacing" : "(!_frontFacing)");
329 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400330 default:
331 if (Variable::kGlobal_Storage == ref.fVariable.fStorage) {
332 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
333 this->write("_in.");
334 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400335 this->write("_out->");
Timothy Lianga06f2152018-05-24 15:33:31 -0400336 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
337 ref.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400338 this->write("_uniforms.");
339 } else {
Timothy Liangee84fe12018-05-18 14:38:19 -0400340 this->write("_globals->");
Ethan Nicholascc305772017-10-13 16:17:45 -0400341 }
342 }
Timothy Liang651286f2018-06-07 09:55:33 -0400343 this->writeName(ref.fVariable.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400344 }
345}
346
347void MetalCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
348 this->writeExpression(*expr.fBase, kPostfix_Precedence);
349 this->write("[");
350 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
351 this->write("]");
352}
353
354void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
Timothy Liang7d637782018-06-05 09:58:07 -0400355 const Type::Field* field = &f.fBase->fType.fields()[f.fFieldIndex];
Ethan Nicholascc305772017-10-13 16:17:45 -0400356 if (FieldAccess::kDefault_OwnerKind == f.fOwnerKind) {
357 this->writeExpression(*f.fBase, kPostfix_Precedence);
358 this->write(".");
359 }
Timothy Liang7d637782018-06-05 09:58:07 -0400360 switch (field->fModifiers.fLayout.fBuiltin) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400361 case SK_CLIPDISTANCE_BUILTIN:
362 this->write("gl_ClipDistance");
363 break;
364 case SK_POSITION_BUILTIN:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400365 this->write("_out->sk_Position");
Ethan Nicholascc305772017-10-13 16:17:45 -0400366 break;
367 default:
Timothy Liang7d637782018-06-05 09:58:07 -0400368 if (field->fName == "sk_PointSize") {
369 this->write("_out->sk_PointSize");
370 } else {
371 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
372 this->write("_globals->");
373 this->write(fInterfaceBlockNameMap[fInterfaceBlockMap[field]]);
374 this->write("->");
375 }
Timothy Liang651286f2018-06-07 09:55:33 -0400376 this->writeName(field->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400377 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400378 }
379}
380
381void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
382 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
383 this->write(".");
384 for (int c : swizzle.fComponents) {
385 this->write(&("x\0y\0z\0w\0"[c * 2]));
386 }
387}
388
389MetalCodeGenerator::Precedence MetalCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
390 switch (op) {
391 case Token::STAR: // fall through
392 case Token::SLASH: // fall through
393 case Token::PERCENT: return MetalCodeGenerator::kMultiplicative_Precedence;
394 case Token::PLUS: // fall through
395 case Token::MINUS: return MetalCodeGenerator::kAdditive_Precedence;
396 case Token::SHL: // fall through
397 case Token::SHR: return MetalCodeGenerator::kShift_Precedence;
398 case Token::LT: // fall through
399 case Token::GT: // fall through
400 case Token::LTEQ: // fall through
401 case Token::GTEQ: return MetalCodeGenerator::kRelational_Precedence;
402 case Token::EQEQ: // fall through
403 case Token::NEQ: return MetalCodeGenerator::kEquality_Precedence;
404 case Token::BITWISEAND: return MetalCodeGenerator::kBitwiseAnd_Precedence;
405 case Token::BITWISEXOR: return MetalCodeGenerator::kBitwiseXor_Precedence;
406 case Token::BITWISEOR: return MetalCodeGenerator::kBitwiseOr_Precedence;
407 case Token::LOGICALAND: return MetalCodeGenerator::kLogicalAnd_Precedence;
408 case Token::LOGICALXOR: return MetalCodeGenerator::kLogicalXor_Precedence;
409 case Token::LOGICALOR: return MetalCodeGenerator::kLogicalOr_Precedence;
410 case Token::EQ: // fall through
411 case Token::PLUSEQ: // fall through
412 case Token::MINUSEQ: // fall through
413 case Token::STAREQ: // fall through
414 case Token::SLASHEQ: // fall through
415 case Token::PERCENTEQ: // fall through
416 case Token::SHLEQ: // fall through
417 case Token::SHREQ: // fall through
418 case Token::LOGICALANDEQ: // fall through
419 case Token::LOGICALXOREQ: // fall through
420 case Token::LOGICALOREQ: // fall through
421 case Token::BITWISEANDEQ: // fall through
422 case Token::BITWISEXOREQ: // fall through
423 case Token::BITWISEOREQ: return MetalCodeGenerator::kAssignment_Precedence;
424 case Token::COMMA: return MetalCodeGenerator::kSequence_Precedence;
425 default: ABORT("unsupported binary operator");
426 }
427}
428
429void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
430 Precedence parentPrecedence) {
431 Precedence precedence = GetBinaryPrecedence(b.fOperator);
432 if (precedence >= parentPrecedence) {
433 this->write("(");
434 }
435 if (Compiler::IsAssignment(b.fOperator) &&
436 Expression::kVariableReference_Kind == b.fLeft->fKind &&
437 Variable::kParameter_Storage == ((VariableReference&) *b.fLeft).fVariable.fStorage &&
438 (((VariableReference&) *b.fLeft).fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) {
439 // writing to an out parameter. Since we have to turn those into pointers, we have to
440 // dereference it here.
441 this->write("*");
442 }
443 this->writeExpression(*b.fLeft, precedence);
444 if (b.fOperator != Token::EQ && Compiler::IsAssignment(b.fOperator) &&
445 Expression::kSwizzle_Kind == b.fLeft->fKind && !b.fLeft->hasSideEffects()) {
446 // This doesn't compile in Metal:
447 // float4 x = float4(1);
448 // x.xy *= float2x2(...);
449 // with the error message "non-const reference cannot bind to vector element",
450 // but switching it to x.xy = x.xy * float2x2(...) fixes it. We perform this tranformation
451 // as long as the LHS has no side effects, and hope for the best otherwise.
452 this->write(" = ");
453 this->writeExpression(*b.fLeft, kAssignment_Precedence);
454 this->write(" ");
455 String op = Compiler::OperatorName(b.fOperator);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400456 SkASSERT(op.endsWith("="));
Ethan Nicholascc305772017-10-13 16:17:45 -0400457 this->write(op.substr(0, op.size() - 1).c_str());
458 this->write(" ");
459 } else {
460 this->write(String(" ") + Compiler::OperatorName(b.fOperator) + " ");
461 }
462 this->writeExpression(*b.fRight, precedence);
463 if (precedence >= parentPrecedence) {
464 this->write(")");
465 }
466}
467
468void MetalCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
469 Precedence parentPrecedence) {
470 if (kTernary_Precedence >= parentPrecedence) {
471 this->write("(");
472 }
473 this->writeExpression(*t.fTest, kTernary_Precedence);
474 this->write(" ? ");
475 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
476 this->write(" : ");
477 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
478 if (kTernary_Precedence >= parentPrecedence) {
479 this->write(")");
480 }
481}
482
483void MetalCodeGenerator::writePrefixExpression(const PrefixExpression& p,
484 Precedence parentPrecedence) {
485 if (kPrefix_Precedence >= parentPrecedence) {
486 this->write("(");
487 }
488 this->write(Compiler::OperatorName(p.fOperator));
489 this->writeExpression(*p.fOperand, kPrefix_Precedence);
490 if (kPrefix_Precedence >= parentPrecedence) {
491 this->write(")");
492 }
493}
494
495void MetalCodeGenerator::writePostfixExpression(const PostfixExpression& p,
496 Precedence parentPrecedence) {
497 if (kPostfix_Precedence >= parentPrecedence) {
498 this->write("(");
499 }
500 this->writeExpression(*p.fOperand, kPostfix_Precedence);
501 this->write(Compiler::OperatorName(p.fOperator));
502 if (kPostfix_Precedence >= parentPrecedence) {
503 this->write(")");
504 }
505}
506
507void MetalCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
508 this->write(b.fValue ? "true" : "false");
509}
510
511void MetalCodeGenerator::writeIntLiteral(const IntLiteral& i) {
512 if (i.fType == *fContext.fUInt_Type) {
513 this->write(to_string(i.fValue & 0xffffffff) + "u");
514 } else {
515 this->write(to_string((int32_t) i.fValue));
516 }
517}
518
519void MetalCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
520 this->write(to_string(f.fValue));
521}
522
523void MetalCodeGenerator::writeSetting(const Setting& s) {
524 ABORT("internal error; setting was not folded to a constant during compilation\n");
525}
526
527void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
528 const char* separator = "";
529 if ("main" == f.fDeclaration.fName) {
530 switch (fProgram.fKind) {
531 case Program::kFragment_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400532#ifdef SK_MOLTENVK
533 this->write("fragment Outputs main0");
534#else
535 this->write("fragment Outputs fragmentMain");
536#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400537 break;
538 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400539#ifdef SK_MOLTENVK
Timothy Lianga06f2152018-05-24 15:33:31 -0400540 this->write("vertex Outputs main0");
Timothy Liangb8eeb802018-07-23 16:46:16 -0400541#else
542 this->write("vertex Outputs vertexMain");
543#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400544 break;
545 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400546 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400547 }
548 this->write("(Inputs _in [[stage_in]]");
549 if (-1 != fUniformBuffer) {
550 this->write(", constant Uniforms& _uniforms [[buffer(" +
551 to_string(fUniformBuffer) + ")]]");
552 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400553 for (const auto& e : fProgram) {
554 if (ProgramElement::kVar_Kind == e.fKind) {
555 VarDeclarations& decls = (VarDeclarations&) e;
556 if (!decls.fVars.size()) {
557 continue;
558 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400559 for (const auto& stmt: decls.fVars) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400560 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400561 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
Timothy Liang7d637782018-06-05 09:58:07 -0400562 this->write(", texture2d<float> "); // FIXME - support other texture types
Timothy Liang651286f2018-06-07 09:55:33 -0400563 this->writeName(var.fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400564 this->write("[[texture(");
Timothy Lianga06f2152018-05-24 15:33:31 -0400565 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
566 this->write(")]]");
567 this->write(", sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -0400568 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400569 this->write(SAMPLER_SUFFIX);
570 this->write("[[sampler(");
571 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
Timothy Liangee84fe12018-05-18 14:38:19 -0400572 this->write(")]]");
573 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400574 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400575 } else if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
576 InterfaceBlock& intf = (InterfaceBlock&) e;
577 if ("sk_PerVertex" == intf.fTypeName) {
578 continue;
579 }
580 this->write(", constant ");
581 this->writeType(intf.fVariable.fType);
582 this->write("& " );
583 this->write(fInterfaceBlockNameMap[&intf]);
584 this->write(" [[buffer(");
Timothy Liang057c3902018-08-08 10:48:45 -0400585#ifdef SK_MOLTENVK
Timothy Liang7d637782018-06-05 09:58:07 -0400586 this->write(to_string(intf.fVariable.fModifiers.fLayout.fSet));
Timothy Liang057c3902018-08-08 10:48:45 -0400587#else
588 this->write(to_string(intf.fVariable.fModifiers.fLayout.fBinding));
589#endif
Timothy Lianga06f2152018-05-24 15:33:31 -0400590 this->write(")]]");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400591 }
592 }
Timothy Liang5422f9a2018-08-10 10:57:55 -0400593 if (fProgram.fKind == Program::kFragment_Kind) {
594 if (fInterfaceBlockNameMap.empty()) {
Timothy Liangb8eeb802018-07-23 16:46:16 -0400595 // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
596 // in a different way altogether.
Timothy Liang5422f9a2018-08-10 10:57:55 -0400597#ifdef SK_MOLTENVK
598 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(0)]]");
599#else
600 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
601#endif
602 }
Timothy Liang7b8875d2018-08-10 09:42:31 -0400603 this->write(", bool _frontFacing [[front_facing]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400604 this->write(", float4 _fragCoord [[position]]");
Timothy Liangdc89f192018-06-13 09:20:31 -0400605 } else if (fProgram.fKind == Program::kVertex_Kind) {
606 this->write(", uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400607 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400608 separator = ", ";
609 } else {
610 this->writeType(f.fDeclaration.fReturnType);
Timothy Liang651286f2018-06-07 09:55:33 -0400611 this->write(" ");
612 this->writeName(f.fDeclaration.fName);
613 this->write("(");
Ethan Nicholascc305772017-10-13 16:17:45 -0400614 if (this->requirements(f.fDeclaration) & kInputs_Requirement) {
615 this->write("Inputs _in");
616 separator = ", ";
617 }
618 if (this->requirements(f.fDeclaration) & kOutputs_Requirement) {
619 this->write(separator);
Timothy Liangee84fe12018-05-18 14:38:19 -0400620 this->write("thread Outputs* _out");
Ethan Nicholascc305772017-10-13 16:17:45 -0400621 separator = ", ";
622 }
623 if (this->requirements(f.fDeclaration) & kUniforms_Requirement) {
624 this->write(separator);
625 this->write("Uniforms _uniforms");
626 separator = ", ";
627 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400628 if (this->requirements(f.fDeclaration) & kGlobals_Requirement) {
629 this->write(separator);
630 this->write("thread Globals* _globals");
631 separator = ", ";
632 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400633 }
634 for (const auto& param : f.fDeclaration.fParameters) {
635 this->write(separator);
636 separator = ", ";
637 this->writeModifiers(param->fModifiers, false);
638 std::vector<int> sizes;
639 const Type* type = &param->fType;
640 while (Type::kArray_Kind == type->kind()) {
641 sizes.push_back(type->columns());
642 type = &type->componentType();
643 }
644 this->writeType(*type);
645 if (param->fModifiers.fFlags & Modifiers::kOut_Flag) {
646 this->write("*");
647 }
Timothy Liang651286f2018-06-07 09:55:33 -0400648 this->write(" ");
649 this->writeName(param->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400650 for (int s : sizes) {
651 if (s <= 0) {
652 this->write("[]");
653 } else {
654 this->write("[" + to_string(s) + "]");
655 }
656 }
657 }
658 this->writeLine(") {");
659
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400660 SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
Brian Salomondc092132018-04-04 10:14:16 -0400661
Ethan Nicholascc305772017-10-13 16:17:45 -0400662 if ("main" == f.fDeclaration.fName) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400663 if (fNeedsGlobalStructInit) {
664 this->writeLine(" Globals globalStruct;");
665 this->writeLine(" thread Globals* _globals = &globalStruct;");
Timothy Liang7d637782018-06-05 09:58:07 -0400666 for (const auto& intf: fInterfaceBlockNameMap) {
667 const auto& intfName = intf.second;
668 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400669 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400670 this->write(" = &");
Timothy Liang651286f2018-06-07 09:55:33 -0400671 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400672 this->write(";\n");
673 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400674 for (const auto& var: fInitNonConstGlobalVars) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400675 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400676 this->writeName(var->fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400677 this->write(" = ");
678 this->writeVarInitializer(*var->fVar, *var->fValue);
679 this->writeLine(";");
680 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400681 for (const auto& texture: fTextures) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400682 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400683 this->writeName(texture->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400684 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400685 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400686 this->write(";\n");
687 this->write(" _globals->");
Timothy Liang651286f2018-06-07 09:55:33 -0400688 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400689 this->write(SAMPLER_SUFFIX);
690 this->write(" = ");
Timothy Liang651286f2018-06-07 09:55:33 -0400691 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400692 this->write(SAMPLER_SUFFIX);
Timothy Liangee84fe12018-05-18 14:38:19 -0400693 this->write(";\n");
694 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400695 }
Timothy Liang7d637782018-06-05 09:58:07 -0400696 this->writeLine(" Outputs _outputStruct;");
697 this->writeLine(" thread Outputs* _out = &_outputStruct;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400698 }
699 fFunctionHeader = "";
700 OutputStream* oldOut = fOut;
701 StringStream buffer;
702 fOut = &buffer;
703 fIndentation++;
704 this->writeStatements(((Block&) *f.fBody).fStatements);
705 if ("main" == f.fDeclaration.fName) {
706 switch (fProgram.fKind) {
707 case Program::kFragment_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -0400708 this->writeLine("return *_out;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400709 break;
710 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400711 this->writeLine("_out->sk_Position.y = -_out->sk_Position.y;");
Timothy Lianga06f2152018-05-24 15:33:31 -0400712 this->writeLine("return *_out;"); // FIXME - detect if function already has return
Ethan Nicholascc305772017-10-13 16:17:45 -0400713 break;
714 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400715 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400716 }
717 }
718 fIndentation--;
719 this->writeLine("}");
720
721 fOut = oldOut;
722 this->write(fFunctionHeader);
723 this->write(buffer.str());
724}
725
726void MetalCodeGenerator::writeModifiers(const Modifiers& modifiers,
727 bool globalContext) {
728 if (modifiers.fFlags & Modifiers::kOut_Flag) {
729 this->write("thread ");
730 }
731 if (modifiers.fFlags & Modifiers::kConst_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400732 this->write("constant ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400733 }
734}
735
736void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
737 if ("sk_PerVertex" == intf.fTypeName) {
738 return;
739 }
740 this->writeModifiers(intf.fVariable.fModifiers, true);
Timothy Liangdc89f192018-06-13 09:20:31 -0400741 this->write("struct ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400742 this->writeLine(intf.fTypeName + " {");
Ethan Nicholascc305772017-10-13 16:17:45 -0400743 const Type* structType = &intf.fVariable.fType;
Timothy Lianga06f2152018-05-24 15:33:31 -0400744 fWrittenStructs.push_back(structType);
Ethan Nicholascc305772017-10-13 16:17:45 -0400745 while (Type::kArray_Kind == structType->kind()) {
746 structType = &structType->componentType();
747 }
Timothy Liangdc89f192018-06-13 09:20:31 -0400748 fIndentation++;
749 writeFields(structType->fields(), structType->fOffset, &intf);
Timothy Liang7d637782018-06-05 09:58:07 -0400750 if (fProgram.fKind == Program::kFragment_Kind) {
751 this->writeLine("float u_skRTHeight;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400752 }
753 fIndentation--;
754 this->write("}");
755 if (intf.fInstanceName.size()) {
756 this->write(" ");
757 this->write(intf.fInstanceName);
758 for (const auto& size : intf.fSizes) {
759 this->write("[");
760 if (size) {
761 this->writeExpression(*size, kTopLevel_Precedence);
762 }
763 this->write("]");
764 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400765 fInterfaceBlockNameMap[&intf] = intf.fInstanceName;
766 } else {
Timothy Liang7d637782018-06-05 09:58:07 -0400767 fInterfaceBlockNameMap[&intf] = "_anonInterface" + to_string(fAnonInterfaceCount++);
Ethan Nicholascc305772017-10-13 16:17:45 -0400768 }
769 this->writeLine(";");
770}
771
Timothy Liangdc89f192018-06-13 09:20:31 -0400772void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int parentOffset,
773 const InterfaceBlock* parentIntf) {
774 MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
775 int currentOffset = 0;
776 for (const auto& field: fields) {
777 int fieldOffset = field.fModifiers.fLayout.fOffset;
778 const Type* fieldType = field.fType;
779 if (fieldOffset != -1) {
780 if (currentOffset > fieldOffset) {
781 fErrors.error(parentOffset,
782 "offset of field '" + field.fName + "' must be at least " +
783 to_string((int) currentOffset));
784 } else if (currentOffset < fieldOffset) {
785 this->write("char pad");
786 this->write(to_string(fPaddingCount++));
787 this->write("[");
788 this->write(to_string(fieldOffset - currentOffset));
789 this->writeLine("];");
790 currentOffset = fieldOffset;
791 }
792 int alignment = memoryLayout.alignment(*fieldType);
793 if (fieldOffset % alignment) {
794 fErrors.error(parentOffset,
795 "offset of field '" + field.fName + "' must be a multiple of " +
796 to_string((int) alignment));
797 }
798 }
799 if (fieldType->kind() == Type::kVector_Kind &&
800 fieldType->columns() == 3) {
801 // Pack all vec3 types so that their size in bytes will match what was expected in the
802 // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
803 // has vec3 equal to 3 * component type.
804 this->write(PACKED_PREFIX);
805 }
806 currentOffset += memoryLayout.size(*fieldType);
807 std::vector<int> sizes;
808 while (fieldType->kind() == Type::kArray_Kind) {
809 sizes.push_back(fieldType->columns());
810 fieldType = &fieldType->componentType();
811 }
812 this->writeModifiers(field.fModifiers, false);
813 this->writeType(*fieldType);
814 this->write(" ");
815 this->writeName(field.fName);
816 for (int s : sizes) {
817 if (s <= 0) {
818 this->write("[]");
819 } else {
820 this->write("[" + to_string(s) + "]");
821 }
822 }
823 this->writeLine(";");
824 if (parentIntf) {
825 fInterfaceBlockMap[&field] = parentIntf;
826 }
827 }
828}
829
Ethan Nicholascc305772017-10-13 16:17:45 -0400830void MetalCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
831 this->writeExpression(value, kTopLevel_Precedence);
832}
833
Timothy Liang651286f2018-06-07 09:55:33 -0400834void MetalCodeGenerator::writeName(const String& name) {
835 if (fReservedWords.find(name) != fReservedWords.end()) {
836 this->write("_"); // adding underscore before name to avoid conflict with reserved words
837 }
838 this->write(name);
839}
840
Ethan Nicholascc305772017-10-13 16:17:45 -0400841void MetalCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400842 SkASSERT(decl.fVars.size() > 0);
Ethan Nicholascc305772017-10-13 16:17:45 -0400843 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000844 for (const auto& stmt : decl.fVars) {
845 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400846 if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400847 continue;
848 }
849 if (wroteType) {
850 this->write(", ");
851 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000852 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholascc305772017-10-13 16:17:45 -0400853 this->writeType(decl.fBaseType);
854 this->write(" ");
855 wroteType = true;
856 }
Timothy Liang651286f2018-06-07 09:55:33 -0400857 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000858 for (const auto& size : var.fSizes) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400859 this->write("[");
860 if (size) {
861 this->writeExpression(*size, kTopLevel_Precedence);
862 }
863 this->write("]");
864 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000865 if (var.fValue) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400866 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000867 this->writeVarInitializer(*var.fVar, *var.fValue);
Ethan Nicholascc305772017-10-13 16:17:45 -0400868 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000869 if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400870 if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
871 fHeader.writeText("#extension ");
872 fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
873 fHeader.writeText(" : require\n");
874 }
875 fFoundImageDecl = true;
876 }
877 }
878 if (wroteType) {
879 this->write(";");
880 }
881}
882
883void MetalCodeGenerator::writeStatement(const Statement& s) {
884 switch (s.fKind) {
885 case Statement::kBlock_Kind:
886 this->writeBlock((Block&) s);
887 break;
888 case Statement::kExpression_Kind:
889 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
890 this->write(";");
891 break;
892 case Statement::kReturn_Kind:
893 this->writeReturnStatement((ReturnStatement&) s);
894 break;
895 case Statement::kVarDeclarations_Kind:
896 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
897 break;
898 case Statement::kIf_Kind:
899 this->writeIfStatement((IfStatement&) s);
900 break;
901 case Statement::kFor_Kind:
902 this->writeForStatement((ForStatement&) s);
903 break;
904 case Statement::kWhile_Kind:
905 this->writeWhileStatement((WhileStatement&) s);
906 break;
907 case Statement::kDo_Kind:
908 this->writeDoStatement((DoStatement&) s);
909 break;
910 case Statement::kSwitch_Kind:
911 this->writeSwitchStatement((SwitchStatement&) s);
912 break;
913 case Statement::kBreak_Kind:
914 this->write("break;");
915 break;
916 case Statement::kContinue_Kind:
917 this->write("continue;");
918 break;
919 case Statement::kDiscard_Kind:
Timothy Lianga06f2152018-05-24 15:33:31 -0400920 this->write("discard_fragment();");
Ethan Nicholascc305772017-10-13 16:17:45 -0400921 break;
922 case Statement::kNop_Kind:
923 this->write(";");
924 break;
925 default:
926 ABORT("unsupported statement: %s", s.description().c_str());
927 }
928}
929
930void MetalCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
931 for (const auto& s : statements) {
932 if (!s->isEmpty()) {
933 this->writeStatement(*s);
934 this->writeLine();
935 }
936 }
937}
938
939void MetalCodeGenerator::writeBlock(const Block& b) {
940 this->writeLine("{");
941 fIndentation++;
942 this->writeStatements(b.fStatements);
943 fIndentation--;
944 this->write("}");
945}
946
947void MetalCodeGenerator::writeIfStatement(const IfStatement& stmt) {
948 this->write("if (");
949 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
950 this->write(") ");
951 this->writeStatement(*stmt.fIfTrue);
952 if (stmt.fIfFalse) {
953 this->write(" else ");
954 this->writeStatement(*stmt.fIfFalse);
955 }
956}
957
958void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
959 this->write("for (");
960 if (f.fInitializer && !f.fInitializer->isEmpty()) {
961 this->writeStatement(*f.fInitializer);
962 } else {
963 this->write("; ");
964 }
965 if (f.fTest) {
966 this->writeExpression(*f.fTest, kTopLevel_Precedence);
967 }
968 this->write("; ");
969 if (f.fNext) {
970 this->writeExpression(*f.fNext, kTopLevel_Precedence);
971 }
972 this->write(") ");
973 this->writeStatement(*f.fStatement);
974}
975
976void MetalCodeGenerator::writeWhileStatement(const WhileStatement& w) {
977 this->write("while (");
978 this->writeExpression(*w.fTest, kTopLevel_Precedence);
979 this->write(") ");
980 this->writeStatement(*w.fStatement);
981}
982
983void MetalCodeGenerator::writeDoStatement(const DoStatement& d) {
984 this->write("do ");
985 this->writeStatement(*d.fStatement);
986 this->write(" while (");
987 this->writeExpression(*d.fTest, kTopLevel_Precedence);
988 this->write(");");
989}
990
991void MetalCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
992 this->write("switch (");
993 this->writeExpression(*s.fValue, kTopLevel_Precedence);
994 this->writeLine(") {");
995 fIndentation++;
996 for (const auto& c : s.fCases) {
997 if (c->fValue) {
998 this->write("case ");
999 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1000 this->writeLine(":");
1001 } else {
1002 this->writeLine("default:");
1003 }
1004 fIndentation++;
1005 for (const auto& stmt : c->fStatements) {
1006 this->writeStatement(*stmt);
1007 this->writeLine();
1008 }
1009 fIndentation--;
1010 }
1011 fIndentation--;
1012 this->write("}");
1013}
1014
1015void MetalCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1016 this->write("return");
1017 if (r.fExpression) {
1018 this->write(" ");
1019 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1020 }
1021 this->write(";");
1022}
1023
1024void MetalCodeGenerator::writeHeader() {
1025 this->write("#include <metal_stdlib>\n");
1026 this->write("#include <simd/simd.h>\n");
1027 this->write("using namespace metal;\n");
1028}
1029
1030void MetalCodeGenerator::writeUniformStruct() {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001031 for (const auto& e : fProgram) {
1032 if (ProgramElement::kVar_Kind == e.fKind) {
1033 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001034 if (!decls.fVars.size()) {
1035 continue;
1036 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001037 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001038 if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1039 first.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001040 if (-1 == fUniformBuffer) {
1041 this->write("struct Uniforms {\n");
1042 fUniformBuffer = first.fModifiers.fLayout.fSet;
1043 if (-1 == fUniformBuffer) {
1044 fErrors.error(decls.fOffset, "Metal uniforms must have 'layout(set=...)'");
1045 }
1046 } else if (first.fModifiers.fLayout.fSet != fUniformBuffer) {
1047 if (-1 == fUniformBuffer) {
1048 fErrors.error(decls.fOffset, "Metal backend requires all uniforms to have "
1049 "the same 'layout(set=...)'");
1050 }
1051 }
1052 this->write(" ");
1053 this->writeType(first.fType);
1054 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001055 for (const auto& stmt : decls.fVars) {
1056 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001057 this->writeName(var.fVar->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -04001058 }
1059 this->write(";\n");
1060 }
1061 }
1062 }
1063 if (-1 != fUniformBuffer) {
1064 this->write("};\n");
1065 }
1066}
1067
1068void MetalCodeGenerator::writeInputStruct() {
1069 this->write("struct Inputs {\n");
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001070 for (const auto& e : fProgram) {
1071 if (ProgramElement::kVar_Kind == e.fKind) {
1072 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001073 if (!decls.fVars.size()) {
1074 continue;
1075 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001076 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001077 if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
1078 -1 == first.fModifiers.fLayout.fBuiltin) {
1079 this->write(" ");
1080 this->writeType(first.fType);
1081 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001082 for (const auto& stmt : decls.fVars) {
1083 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001084 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001085 if (-1 != var.fVar->fModifiers.fLayout.fLocation) {
Timothy Liang7d637782018-06-05 09:58:07 -04001086 if (fProgram.fKind == Program::kVertex_Kind) {
1087 this->write(" [[attribute(" +
1088 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1089 } else if (fProgram.fKind == Program::kFragment_Kind) {
1090 this->write(" [[user(locn" +
1091 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1092 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001093 }
1094 }
1095 this->write(";\n");
1096 }
1097 }
1098 }
1099 this->write("};\n");
1100}
1101
1102void MetalCodeGenerator::writeOutputStruct() {
1103 this->write("struct Outputs {\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001104 if (fProgram.fKind == Program::kVertex_Kind) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001105 this->write(" float4 sk_Position [[position]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001106 } else if (fProgram.fKind == Program::kFragment_Kind) {
Timothy Liangde0be802018-08-10 13:48:08 -04001107 this->write(" float4 sk_FragColor [[color(0)]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001108 }
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001109 for (const auto& e : fProgram) {
1110 if (ProgramElement::kVar_Kind == e.fKind) {
1111 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001112 if (!decls.fVars.size()) {
1113 continue;
1114 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001115 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001116 if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
1117 -1 == first.fModifiers.fLayout.fBuiltin) {
1118 this->write(" ");
1119 this->writeType(first.fType);
1120 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001121 for (const auto& stmt : decls.fVars) {
1122 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001123 this->writeName(var.fVar->fName);
Timothy Liang7d637782018-06-05 09:58:07 -04001124 if (fProgram.fKind == Program::kVertex_Kind) {
1125 this->write(" [[user(locn" +
1126 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1127 } else if (fProgram.fKind == Program::kFragment_Kind) {
1128 this->write(" [[color(" +
Timothy Liangde0be802018-08-10 13:48:08 -04001129 to_string(var.fVar->fModifiers.fLayout.fLocation) +")");
1130 int colorIndex = var.fVar->fModifiers.fLayout.fIndex;
1131 if (colorIndex) {
1132 this->write(", index(" + to_string(colorIndex) + ")");
1133 }
1134 this->write("]]");
Timothy Liang7d637782018-06-05 09:58:07 -04001135 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001136 }
1137 this->write(";\n");
1138 }
1139 }
Timothy Liang7d637782018-06-05 09:58:07 -04001140 }
1141 if (fProgram.fKind == Program::kVertex_Kind) {
1142 this->write(" float sk_PointSize;\n");
1143 }
1144 this->write("};\n");
1145}
1146
1147void MetalCodeGenerator::writeInterfaceBlocks() {
1148 bool wroteInterfaceBlock = false;
1149 for (const auto& e : fProgram) {
1150 if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
1151 this->writeInterfaceBlock((InterfaceBlock&) e);
1152 wroteInterfaceBlock = true;
1153 }
1154 }
1155 if (!wroteInterfaceBlock && (fProgram.fKind == Program::kFragment_Kind)) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001156 // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
1157 // in a different way altogether.
Timothy Liang7d637782018-06-05 09:58:07 -04001158 this->writeLine("struct sksl_synthetic_uniforms {");
1159 this->writeLine(" float u_skRTHeight;");
1160 this->writeLine("};");
1161 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001162}
1163
Timothy Liangee84fe12018-05-18 14:38:19 -04001164void MetalCodeGenerator::writeGlobalStruct() {
1165 bool wroteStructDecl = false;
Timothy Liang7d637782018-06-05 09:58:07 -04001166 for (const auto& intf : fInterfaceBlockNameMap) {
1167 if (!wroteStructDecl) {
1168 this->write("struct Globals {\n");
1169 wroteStructDecl = true;
1170 }
1171 fNeedsGlobalStructInit = true;
1172 const auto& intfType = intf.first;
1173 const auto& intfName = intf.second;
1174 this->write(" constant ");
1175 this->write(intfType->fTypeName);
1176 this->write("* ");
Timothy Liang651286f2018-06-07 09:55:33 -04001177 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -04001178 this->write(";\n");
1179 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001180 for (const auto& e : fProgram) {
1181 if (ProgramElement::kVar_Kind == e.fKind) {
1182 VarDeclarations& decls = (VarDeclarations&) e;
1183 if (!decls.fVars.size()) {
1184 continue;
1185 }
1186 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001187 if ((!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) ||
1188 first.fType.kind() == Type::kSampler_Kind) {
Timothy Liangee84fe12018-05-18 14:38:19 -04001189 if (!wroteStructDecl) {
1190 this->write("struct Globals {\n");
1191 wroteStructDecl = true;
1192 }
1193 fNeedsGlobalStructInit = true;
1194 this->write(" ");
1195 this->writeType(first.fType);
1196 this->write(" ");
1197 for (const auto& stmt : decls.fVars) {
1198 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001199 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001200 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
1201 fTextures.push_back(var.fVar);
1202 this->write(";\n");
1203 this->write(" sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -04001204 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001205 this->write(SAMPLER_SUFFIX);
1206 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001207 if (var.fValue) {
1208 fInitNonConstGlobalVars.push_back(&var);
1209 }
1210 }
1211 this->write(";\n");
1212 }
1213 }
1214 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001215 if (wroteStructDecl) {
1216 this->write("};\n");
1217 }
1218}
1219
Ethan Nicholascc305772017-10-13 16:17:45 -04001220void MetalCodeGenerator::writeProgramElement(const ProgramElement& e) {
1221 switch (e.fKind) {
1222 case ProgramElement::kExtension_Kind:
1223 break;
1224 case ProgramElement::kVar_Kind: {
1225 VarDeclarations& decl = (VarDeclarations&) e;
1226 if (decl.fVars.size() > 0) {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001227 int builtin = ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
Ethan Nicholascc305772017-10-13 16:17:45 -04001228 if (-1 == builtin) {
1229 // normal var
1230 this->writeVarDeclarations(decl, true);
1231 this->writeLine();
1232 } else if (SK_FRAGCOLOR_BUILTIN == builtin) {
1233 // ignore
1234 }
1235 }
1236 break;
1237 }
1238 case ProgramElement::kInterfaceBlock_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -04001239 // handled in writeInterfaceBlocks, do nothing
Ethan Nicholascc305772017-10-13 16:17:45 -04001240 break;
1241 case ProgramElement::kFunction_Kind:
1242 this->writeFunction((FunctionDefinition&) e);
1243 break;
1244 case ProgramElement::kModifiers_Kind:
1245 this->writeModifiers(((ModifiersDeclaration&) e).fModifiers, true);
1246 this->writeLine(";");
1247 break;
1248 default:
1249 printf("%s\n", e.description().c_str());
1250 ABORT("unsupported program element");
1251 }
1252}
1253
1254MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Expression& e) {
1255 switch (e.fKind) {
1256 case Expression::kFunctionCall_Kind: {
1257 const FunctionCall& f = (const FunctionCall&) e;
1258 Requirements result = this->requirements(f.fFunction);
1259 for (const auto& e : f.fArguments) {
1260 result |= this->requirements(*e);
1261 }
1262 return result;
1263 }
1264 case Expression::kConstructor_Kind: {
1265 const Constructor& c = (const Constructor&) e;
1266 Requirements result = kNo_Requirements;
1267 for (const auto& e : c.fArguments) {
1268 result |= this->requirements(*e);
1269 }
1270 return result;
1271 }
Timothy Liang7d637782018-06-05 09:58:07 -04001272 case Expression::kFieldAccess_Kind: {
1273 const FieldAccess& f = (const FieldAccess&) e;
1274 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
1275 return kGlobals_Requirement;
1276 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001277 return this->requirements(*((const FieldAccess&) e).fBase);
Timothy Liang7d637782018-06-05 09:58:07 -04001278 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001279 case Expression::kSwizzle_Kind:
1280 return this->requirements(*((const Swizzle&) e).fBase);
1281 case Expression::kBinary_Kind: {
1282 const BinaryExpression& b = (const BinaryExpression&) e;
1283 return this->requirements(*b.fLeft) | this->requirements(*b.fRight);
1284 }
1285 case Expression::kIndex_Kind: {
1286 const IndexExpression& idx = (const IndexExpression&) e;
1287 return this->requirements(*idx.fBase) | this->requirements(*idx.fIndex);
1288 }
1289 case Expression::kPrefix_Kind:
1290 return this->requirements(*((const PrefixExpression&) e).fOperand);
1291 case Expression::kPostfix_Kind:
1292 return this->requirements(*((const PostfixExpression&) e).fOperand);
1293 case Expression::kTernary_Kind: {
1294 const TernaryExpression& t = (const TernaryExpression&) e;
1295 return this->requirements(*t.fTest) | this->requirements(*t.fIfTrue) |
1296 this->requirements(*t.fIfFalse);
1297 }
1298 case Expression::kVariableReference_Kind: {
1299 const VariableReference& v = (const VariableReference&) e;
1300 Requirements result = kNo_Requirements;
1301 if (v.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
1302 result = kInputs_Requirement;
1303 } else if (Variable::kGlobal_Storage == v.fVariable.fStorage) {
1304 if (v.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
1305 result = kInputs_Requirement;
1306 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
1307 result = kOutputs_Requirement;
Timothy Lianga06f2152018-05-24 15:33:31 -04001308 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1309 v.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001310 result = kUniforms_Requirement;
Timothy Liangee84fe12018-05-18 14:38:19 -04001311 } else {
1312 result = kGlobals_Requirement;
Ethan Nicholascc305772017-10-13 16:17:45 -04001313 }
1314 }
1315 return result;
1316 }
1317 default:
1318 return kNo_Requirements;
1319 }
1320}
1321
1322MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Statement& s) {
1323 switch (s.fKind) {
1324 case Statement::kBlock_Kind: {
1325 Requirements result = kNo_Requirements;
1326 for (const auto& child : ((const Block&) s).fStatements) {
1327 result |= this->requirements(*child);
1328 }
1329 return result;
1330 }
Timothy Liang7d637782018-06-05 09:58:07 -04001331 case Statement::kVarDeclaration_Kind: {
1332 Requirements result = kNo_Requirements;
1333 const VarDeclaration& var = (const VarDeclaration&) s;
1334 if (var.fValue) {
1335 result = this->requirements(*var.fValue);
1336 }
1337 return result;
1338 }
1339 case Statement::kVarDeclarations_Kind: {
1340 Requirements result = kNo_Requirements;
1341 const VarDeclarations& decls = *((const VarDeclarationsStatement&) s).fDeclaration;
1342 for (const auto& stmt : decls.fVars) {
1343 result |= this->requirements(*stmt);
1344 }
1345 return result;
1346 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001347 case Statement::kExpression_Kind:
1348 return this->requirements(*((const ExpressionStatement&) s).fExpression);
1349 case Statement::kReturn_Kind: {
1350 const ReturnStatement& r = (const ReturnStatement&) s;
1351 if (r.fExpression) {
1352 return this->requirements(*r.fExpression);
1353 }
1354 return kNo_Requirements;
1355 }
1356 case Statement::kIf_Kind: {
1357 const IfStatement& i = (const IfStatement&) s;
1358 return this->requirements(*i.fTest) |
1359 this->requirements(*i.fIfTrue) |
1360 (i.fIfFalse && this->requirements(*i.fIfFalse));
1361 }
1362 case Statement::kFor_Kind: {
1363 const ForStatement& f = (const ForStatement&) s;
1364 return this->requirements(*f.fInitializer) |
1365 this->requirements(*f.fTest) |
1366 this->requirements(*f.fNext) |
1367 this->requirements(*f.fStatement);
1368 }
1369 case Statement::kWhile_Kind: {
1370 const WhileStatement& w = (const WhileStatement&) s;
1371 return this->requirements(*w.fTest) |
1372 this->requirements(*w.fStatement);
1373 }
1374 case Statement::kDo_Kind: {
1375 const DoStatement& d = (const DoStatement&) s;
1376 return this->requirements(*d.fTest) |
1377 this->requirements(*d.fStatement);
1378 }
1379 case Statement::kSwitch_Kind: {
1380 const SwitchStatement& sw = (const SwitchStatement&) s;
1381 Requirements result = this->requirements(*sw.fValue);
1382 for (const auto& c : sw.fCases) {
1383 for (const auto& st : c->fStatements) {
1384 result |= this->requirements(*st);
1385 }
1386 }
1387 return result;
1388 }
1389 default:
1390 return kNo_Requirements;
1391 }
1392}
1393
1394MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const FunctionDeclaration& f) {
1395 if (f.fBuiltin) {
1396 return kNo_Requirements;
1397 }
1398 auto found = fRequirements.find(&f);
1399 if (found == fRequirements.end()) {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001400 for (const auto& e : fProgram) {
1401 if (ProgramElement::kFunction_Kind == e.fKind) {
1402 const FunctionDefinition& def = (const FunctionDefinition&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001403 if (&def.fDeclaration == &f) {
1404 Requirements reqs = this->requirements(*def.fBody);
1405 fRequirements[&f] = reqs;
1406 return reqs;
1407 }
1408 }
1409 }
1410 }
1411 return found->second;
1412}
1413
Timothy Liangb8eeb802018-07-23 16:46:16 -04001414bool MetalCodeGenerator::generateCode() {
Ethan Nicholascc305772017-10-13 16:17:45 -04001415 OutputStream* rawOut = fOut;
1416 fOut = &fHeader;
Timothy Liangb8eeb802018-07-23 16:46:16 -04001417#ifdef SK_MOLTENVK
1418 fOut->write((const char*) &MVKMagicNum, sizeof(MVKMagicNum));
1419#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001420 fProgramKind = fProgram.fKind;
1421 this->writeHeader();
1422 this->writeUniformStruct();
1423 this->writeInputStruct();
Timothy Liang7d637782018-06-05 09:58:07 -04001424 this->writeOutputStruct();
1425 this->writeInterfaceBlocks();
Timothy Liangee84fe12018-05-18 14:38:19 -04001426 this->writeGlobalStruct();
Ethan Nicholascc305772017-10-13 16:17:45 -04001427 StringStream body;
1428 fOut = &body;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001429 for (const auto& e : fProgram) {
1430 this->writeProgramElement(e);
Ethan Nicholascc305772017-10-13 16:17:45 -04001431 }
1432 fOut = rawOut;
1433
1434 write_stringstream(fHeader, *rawOut);
1435 write_stringstream(body, *rawOut);
Timothy Liangb8eeb802018-07-23 16:46:16 -04001436#ifdef SK_MOLTENVK
1437 this->write("\0");
1438#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001439 return true;
Ethan Nicholascc305772017-10-13 16:17:45 -04001440}
1441
1442}