blob: b4f75e2cd3eb0eb518868fc95c1cf9230096386d [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/sksl/SkSLMetalCodeGenerator.h"
Ethan Nicholascc305772017-10-13 16:17:45 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/sksl/SkSLCompiler.h"
11#include "src/sksl/ir/SkSLExpressionStatement.h"
12#include "src/sksl/ir/SkSLExtension.h"
13#include "src/sksl/ir/SkSLIndexExpression.h"
14#include "src/sksl/ir/SkSLModifiersDeclaration.h"
15#include "src/sksl/ir/SkSLNop.h"
16#include "src/sksl/ir/SkSLVariableReference.h"
Ethan Nicholascc305772017-10-13 16:17:45 -040017
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)
Ethan Nicholas13863662019-07-29 13:05:15 -040027 fIntrinsicMap[String("sample")] = SPECIAL(Texture);
Timothy Liang651286f2018-06-07 09:55:33 -040028 fIntrinsicMap[String("mod")] = SPECIAL(Mod);
Ethan Nicholas0dc80872019-02-08 15:46:24 -050029 fIntrinsicMap[String("equal")] = METAL(Equal);
30 fIntrinsicMap[String("notEqual")] = METAL(NotEqual);
Timothy Lianga06f2152018-05-24 15:33:31 -040031 fIntrinsicMap[String("lessThan")] = METAL(LessThan);
32 fIntrinsicMap[String("lessThanEqual")] = METAL(LessThanEqual);
33 fIntrinsicMap[String("greaterThan")] = METAL(GreaterThan);
34 fIntrinsicMap[String("greaterThanEqual")] = METAL(GreaterThanEqual);
Timothy Liangee84fe12018-05-18 14:38:19 -040035}
36
Ethan Nicholascc305772017-10-13 16:17:45 -040037void MetalCodeGenerator::write(const char* s) {
38 if (!s[0]) {
39 return;
40 }
41 if (fAtLineStart) {
42 for (int i = 0; i < fIndentation; i++) {
43 fOut->writeText(" ");
44 }
45 }
46 fOut->writeText(s);
47 fAtLineStart = false;
48}
49
50void MetalCodeGenerator::writeLine(const char* s) {
51 this->write(s);
52 fOut->writeText(fLineEnding);
53 fAtLineStart = true;
54}
55
56void MetalCodeGenerator::write(const String& s) {
57 this->write(s.c_str());
58}
59
60void MetalCodeGenerator::writeLine(const String& s) {
61 this->writeLine(s.c_str());
62}
63
64void MetalCodeGenerator::writeLine() {
65 this->writeLine("");
66}
67
68void MetalCodeGenerator::writeExtension(const Extension& ext) {
69 this->writeLine("#extension " + ext.fName + " : enable");
70}
71
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -040072void MetalCodeGenerator::writeType(const Type& type) {
Ethan Nicholascc305772017-10-13 16:17:45 -040073 switch (type.kind()) {
74 case Type::kStruct_Kind:
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -040075 for (const Type* search : fWrittenStructs) {
76 if (*search == type) {
77 // already written
78 this->write(type.name());
79 return;
80 }
81 }
82 fWrittenStructs.push_back(&type);
83 this->writeLine("struct " + type.name() + " {");
84 fIndentation++;
85 this->writeFields(type.fields(), type.fOffset);
86 fIndentation--;
87 this->write("}");
88 break;
Ethan Nicholascc305772017-10-13 16:17:45 -040089 case Type::kVector_Kind:
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -040090 this->writeType(type.componentType());
91 this->write(to_string(type.columns()));
92 break;
Timothy Liang43d225f2018-07-19 15:27:13 -040093 case Type::kMatrix_Kind:
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -040094 this->writeType(type.componentType());
95 this->write(to_string(type.columns()));
96 this->write("x");
97 this->write(to_string(type.rows()));
98 break;
Timothy Liangee84fe12018-05-18 14:38:19 -040099 case Type::kSampler_Kind:
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -0400100 this->write("texture2d<float> "); // FIXME - support other texture types;
101 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400102 default:
Timothy Liang43d225f2018-07-19 15:27:13 -0400103 if (type == *fContext.fHalf_Type) {
104 // FIXME - Currently only supporting floats in MSL to avoid type coercion issues.
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -0400105 this->write(fContext.fFloat_Type->name());
Timothy Liang43d225f2018-07-19 15:27:13 -0400106 } else if (type == *fContext.fByte_Type) {
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -0400107 this->write("char");
Timothy Liang43d225f2018-07-19 15:27:13 -0400108 } else if (type == *fContext.fUByte_Type) {
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -0400109 this->write("uchar");
Timothy Liang7d637782018-06-05 09:58:07 -0400110 } else {
Ethan Nicholas5a9a9b82019-09-20 12:59:22 -0400111 this->write(type.name());
Timothy Liang7d637782018-06-05 09:58:07 -0400112 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400113 }
114}
115
116void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
117 switch (expr.fKind) {
118 case Expression::kBinary_Kind:
119 this->writeBinaryExpression((BinaryExpression&) expr, parentPrecedence);
120 break;
121 case Expression::kBoolLiteral_Kind:
122 this->writeBoolLiteral((BoolLiteral&) expr);
123 break;
124 case Expression::kConstructor_Kind:
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500125 this->writeConstructor((Constructor&) expr, parentPrecedence);
Ethan Nicholascc305772017-10-13 16:17:45 -0400126 break;
127 case Expression::kIntLiteral_Kind:
128 this->writeIntLiteral((IntLiteral&) expr);
129 break;
130 case Expression::kFieldAccess_Kind:
131 this->writeFieldAccess(((FieldAccess&) expr));
132 break;
133 case Expression::kFloatLiteral_Kind:
134 this->writeFloatLiteral(((FloatLiteral&) expr));
135 break;
136 case Expression::kFunctionCall_Kind:
137 this->writeFunctionCall((FunctionCall&) expr);
138 break;
139 case Expression::kPrefix_Kind:
140 this->writePrefixExpression((PrefixExpression&) expr, parentPrecedence);
141 break;
142 case Expression::kPostfix_Kind:
143 this->writePostfixExpression((PostfixExpression&) expr, parentPrecedence);
144 break;
145 case Expression::kSetting_Kind:
146 this->writeSetting((Setting&) expr);
147 break;
148 case Expression::kSwizzle_Kind:
149 this->writeSwizzle((Swizzle&) expr);
150 break;
151 case Expression::kVariableReference_Kind:
152 this->writeVariableReference((VariableReference&) expr);
153 break;
154 case Expression::kTernary_Kind:
155 this->writeTernaryExpression((TernaryExpression&) expr, parentPrecedence);
156 break;
157 case Expression::kIndex_Kind:
158 this->writeIndexExpression((IndexExpression&) expr);
159 break;
160 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500161#ifdef SK_DEBUG
Ethan Nicholascc305772017-10-13 16:17:45 -0400162 ABORT("unsupported expression: %s", expr.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500163#endif
164 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400165 }
166}
167
Timothy Liang6403b0e2018-05-17 10:40:04 -0400168void MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c) {
Timothy Liang7d637782018-06-05 09:58:07 -0400169 auto i = fIntrinsicMap.find(c.fFunction.fName);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400170 SkASSERT(i != fIntrinsicMap.end());
Timothy Liang7d637782018-06-05 09:58:07 -0400171 Intrinsic intrinsic = i->second;
172 int32_t intrinsicId = intrinsic.second;
173 switch (intrinsic.first) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400174 case kSpecial_IntrinsicKind:
175 return this->writeSpecialIntrinsic(c, (SpecialIntrinsic) intrinsicId);
Timothy Lianga06f2152018-05-24 15:33:31 -0400176 break;
177 case kMetal_IntrinsicKind:
178 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
179 switch ((MetalIntrinsic) intrinsicId) {
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500180 case kEqual_MetalIntrinsic:
181 this->write(" == ");
182 break;
183 case kNotEqual_MetalIntrinsic:
184 this->write(" != ");
185 break;
Timothy Lianga06f2152018-05-24 15:33:31 -0400186 case kLessThan_MetalIntrinsic:
187 this->write(" < ");
188 break;
189 case kLessThanEqual_MetalIntrinsic:
190 this->write(" <= ");
191 break;
192 case kGreaterThan_MetalIntrinsic:
193 this->write(" > ");
194 break;
195 case kGreaterThanEqual_MetalIntrinsic:
196 this->write(" >= ");
197 break;
198 default:
199 ABORT("unsupported metal intrinsic kind");
200 }
201 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
202 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400203 default:
204 ABORT("unsupported intrinsic kind");
205 }
206}
207
Ethan Nicholascc305772017-10-13 16:17:45 -0400208void MetalCodeGenerator::writeFunctionCall(const FunctionCall& c) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400209 const auto& entry = fIntrinsicMap.find(c.fFunction.fName);
210 if (entry != fIntrinsicMap.end()) {
211 this->writeIntrinsicCall(c);
212 return;
213 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400214 if (c.fFunction.fBuiltin && "atan" == c.fFunction.fName && 2 == c.fArguments.size()) {
215 this->write("atan2");
Timothy Lianga06f2152018-05-24 15:33:31 -0400216 } else if (c.fFunction.fBuiltin && "inversesqrt" == c.fFunction.fName) {
217 this->write("rsqrt");
Chris Daltondba7aab2018-11-15 10:57:49 -0500218 } else if (c.fFunction.fBuiltin && "inverse" == c.fFunction.fName) {
219 SkASSERT(c.fArguments.size() == 1);
220 this->writeInverseHack(*c.fArguments[0]);
Timothy Liang7d637782018-06-05 09:58:07 -0400221 } else if (c.fFunction.fBuiltin && "dFdx" == c.fFunction.fName) {
222 this->write("dfdx");
223 } else if (c.fFunction.fBuiltin && "dFdy" == c.fFunction.fName) {
Chris Daltonb8af5ad2019-02-25 14:54:21 -0700224 // Flipping Y also negates the Y derivatives.
225 this->write((fProgram.fSettings.fFlipY) ? "-dfdy" : "dfdy");
Ethan Nicholascc305772017-10-13 16:17:45 -0400226 } else {
Timothy Liang651286f2018-06-07 09:55:33 -0400227 this->writeName(c.fFunction.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400228 }
229 this->write("(");
230 const char* separator = "";
231 if (this->requirements(c.fFunction) & kInputs_Requirement) {
232 this->write("_in");
233 separator = ", ";
234 }
235 if (this->requirements(c.fFunction) & kOutputs_Requirement) {
236 this->write(separator);
237 this->write("_out");
238 separator = ", ";
239 }
240 if (this->requirements(c.fFunction) & kUniforms_Requirement) {
241 this->write(separator);
242 this->write("_uniforms");
243 separator = ", ";
244 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400245 if (this->requirements(c.fFunction) & kGlobals_Requirement) {
246 this->write(separator);
247 this->write("_globals");
248 separator = ", ";
249 }
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400250 if (this->requirements(c.fFunction) & kFragCoord_Requirement) {
251 this->write(separator);
252 this->write("_fragCoord");
253 separator = ", ";
254 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400255 for (size_t i = 0; i < c.fArguments.size(); ++i) {
256 const Expression& arg = *c.fArguments[i];
257 this->write(separator);
258 separator = ", ";
259 if (c.fFunction.fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag) {
260 this->write("&");
261 }
262 this->writeExpression(arg, kSequence_Precedence);
263 }
264 this->write(")");
265}
266
Chris Daltondba7aab2018-11-15 10:57:49 -0500267void MetalCodeGenerator::writeInverseHack(const Expression& mat) {
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500268 String typeName = mat.fType.name();
269 String name = typeName + "_inverse";
270 if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
Chris Daltondba7aab2018-11-15 10:57:49 -0500271 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
272 fWrittenIntrinsics.insert(name);
273 fExtraFunctions.writeText((
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500274 typeName + " " + name + "(" + typeName + " m) {"
Chris Daltondba7aab2018-11-15 10:57:49 -0500275 " return float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]) * (1/determinant(m));"
276 "}"
277 ).c_str());
278 }
279 }
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500280 else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
281 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
282 fWrittenIntrinsics.insert(name);
283 fExtraFunctions.writeText((
284 typeName + " " + name + "(" + typeName + " m) {"
285 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];"
286 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];"
287 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];"
288 " float b01 = a22 * a11 - a12 * a21;"
289 " float b11 = -a22 * a10 + a12 * a20;"
290 " float b21 = a21 * a10 - a11 * a20;"
291 " float det = a00 * b01 + a01 * b11 + a02 * b21;"
292 " return " + typeName +
293 " (b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),"
294 " b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),"
295 " b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) * "
296 " (1/det);"
297 "}"
298 ).c_str());
299 }
300 }
301 else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
302 if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
303 fWrittenIntrinsics.insert(name);
304 fExtraFunctions.writeText((
305 typeName + " " + name + "(" + typeName + " m) {"
306 " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];"
307 " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];"
308 " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];"
309 " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];"
310 " float b00 = a00 * a11 - a01 * a10;"
311 " float b01 = a00 * a12 - a02 * a10;"
312 " float b02 = a00 * a13 - a03 * a10;"
313 " float b03 = a01 * a12 - a02 * a11;"
314 " float b04 = a01 * a13 - a03 * a11;"
315 " float b05 = a02 * a13 - a03 * a12;"
316 " float b06 = a20 * a31 - a21 * a30;"
317 " float b07 = a20 * a32 - a22 * a30;"
318 " float b08 = a20 * a33 - a23 * a30;"
319 " float b09 = a21 * a32 - a22 * a31;"
320 " float b10 = a21 * a33 - a23 * a31;"
321 " float b11 = a22 * a33 - a23 * a32;"
322 " float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - "
323 " b04 * b07 + b05 * b06;"
324 " return " + typeName + "(a11 * b11 - a12 * b10 + a13 * b09,"
325 " a02 * b10 - a01 * b11 - a03 * b09,"
326 " a31 * b05 - a32 * b04 + a33 * b03,"
327 " a22 * b04 - a21 * b05 - a23 * b03,"
328 " a12 * b08 - a10 * b11 - a13 * b07,"
329 " a00 * b11 - a02 * b08 + a03 * b07,"
330 " a32 * b02 - a30 * b05 - a33 * b01,"
331 " a20 * b05 - a22 * b02 + a23 * b01,"
332 " a10 * b10 - a11 * b08 + a13 * b06,"
333 " a01 * b08 - a00 * b10 - a03 * b06,"
334 " a30 * b04 - a31 * b02 + a33 * b00,"
335 " a21 * b02 - a20 * b04 - a23 * b00,"
336 " a11 * b07 - a10 * b09 - a12 * b06,"
337 " a00 * b09 - a01 * b07 + a02 * b06,"
338 " a31 * b01 - a30 * b03 - a32 * b00,"
339 " a20 * b03 - a21 * b01 + a22 * b00) / det;"
340 "}"
341 ).c_str());
342 }
343 }
Chris Daltondba7aab2018-11-15 10:57:49 -0500344 this->write(name);
345}
346
Timothy Liang6403b0e2018-05-17 10:40:04 -0400347void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
348 switch (kind) {
349 case kTexture_SpecialIntrinsic:
Timothy Liangee84fe12018-05-18 14:38:19 -0400350 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
Timothy Lianga06f2152018-05-24 15:33:31 -0400351 this->write(".sample(");
352 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
353 this->write(SAMPLER_SUFFIX);
354 this->write(", ");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400355 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
Timothy Liangee84fe12018-05-18 14:38:19 -0400356 if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
357 this->write(".xy)"); // FIXME - add projection functionality
358 } else {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400359 SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
Timothy Liangee84fe12018-05-18 14:38:19 -0400360 this->write(")");
361 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400362 break;
Timothy Liang651286f2018-06-07 09:55:33 -0400363 case kMod_SpecialIntrinsic:
364 // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
365 this->write("((");
366 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
367 this->write(") - (");
368 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
369 this->write(") * floor((");
370 this->writeExpression(*c.fArguments[0], kSequence_Precedence);
371 this->write(") / (");
372 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
373 this->write(")))");
374 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400375 default:
376 ABORT("unsupported special intrinsic kind");
377 }
378}
379
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500380// If it hasn't already been written, writes a constructor for 'matrix' which takes a single value
381// of type 'arg'.
382String MetalCodeGenerator::getMatrixConstructHelper(const Type& matrix, const Type& arg) {
383 String key = matrix.name() + arg.name();
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500384 auto found = fHelpers.find(key);
385 if (found != fHelpers.end()) {
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500386 return found->second;
Ethan Nicholascc305772017-10-13 16:17:45 -0400387 }
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500388 String name;
389 int columns = matrix.columns();
390 int rows = matrix.rows();
391 if (arg.isNumber()) {
392 // creating a matrix from a single scalar value
393 name = "float" + to_string(columns) + "x" + to_string(rows) + "_from_float";
394 fExtraFunctions.printf("float%dx%d %s(float x) {\n",
395 columns, rows, name.c_str());
396 fExtraFunctions.printf(" return float%dx%d(", columns, rows);
397 for (int i = 0; i < columns; ++i) {
398 if (i > 0) {
399 fExtraFunctions.writeText(", ");
400 }
401 fExtraFunctions.printf("float%d(", rows);
402 for (int j = 0; j < rows; ++j) {
403 if (j > 0) {
404 fExtraFunctions.writeText(", ");
405 }
406 if (i == j) {
407 fExtraFunctions.writeText("x");
408 } else {
409 fExtraFunctions.writeText("0");
410 }
411 }
412 fExtraFunctions.writeText(")");
413 }
414 fExtraFunctions.writeText(");\n}\n");
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500415 } else if (arg.kind() == Type::kMatrix_Kind) {
416 // creating a matrix from another matrix
417 int argColumns = arg.columns();
418 int argRows = arg.rows();
419 name = "float" + to_string(columns) + "x" + to_string(rows) + "_from_float" +
420 to_string(argColumns) + "x" + to_string(argRows);
421 fExtraFunctions.printf("float%dx%d %s(float%dx%d m) {\n",
422 columns, rows, name.c_str(), argColumns, argRows);
423 fExtraFunctions.printf(" return float%dx%d(", columns, rows);
424 for (int i = 0; i < columns; ++i) {
425 if (i > 0) {
426 fExtraFunctions.writeText(", ");
427 }
428 fExtraFunctions.printf("float%d(", rows);
429 for (int j = 0; j < rows; ++j) {
430 if (j > 0) {
431 fExtraFunctions.writeText(", ");
432 }
433 if (i < argColumns && j < argRows) {
434 fExtraFunctions.printf("m[%d][%d]", i, j);
435 } else {
436 fExtraFunctions.writeText("0");
437 }
438 }
439 fExtraFunctions.writeText(")");
440 }
441 fExtraFunctions.writeText(");\n}\n");
442 } else if (matrix.rows() == 2 && matrix.columns() == 2 && arg == *fContext.fFloat4_Type) {
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500443 // float2x2(float4) doesn't work, need to split it into float2x2(float2, float2)
444 name = "float2x2_from_float4";
445 fExtraFunctions.printf(
446 "float2x2 %s(float4 v) {\n"
447 " return float2x2(float2(v[0], v[1]), float2(v[2], v[3]));\n"
448 "}\n",
449 name.c_str()
450 );
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500451 } else {
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500452 SkASSERT(false);
453 name = "<error>";
454 }
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500455 fHelpers[key] = name;
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500456 return name;
457}
458
459bool MetalCodeGenerator::canCoerce(const Type& t1, const Type& t2) {
460 if (t1.columns() != t2.columns() || t1.rows() != t2.rows()) {
461 return false;
462 }
463 if (t1.columns() > 1) {
464 return this->canCoerce(t1.componentType(), t2.componentType());
465 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500466 return t1.isFloat() && t2.isFloat();
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500467}
468
469void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
470 if (c.fArguments.size() == 1 && this->canCoerce(c.fType, c.fArguments[0]->fType)) {
471 this->writeExpression(*c.fArguments[0], parentPrecedence);
472 return;
473 }
474 if (c.fType.kind() == Type::kMatrix_Kind && c.fArguments.size() == 1) {
475 const Expression& arg = *c.fArguments[0];
476 String name = this->getMatrixConstructHelper(c.fType, arg.fType);
477 this->write(name);
478 this->write("(");
479 this->writeExpression(arg, kSequence_Precedence);
480 this->write(")");
481 } else {
482 this->writeType(c.fType);
483 this->write("(");
484 const char* separator = "";
485 int scalarCount = 0;
486 for (const auto& arg : c.fArguments) {
487 this->write(separator);
488 separator = ", ";
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500489 if (Type::kMatrix_Kind == c.fType.kind() && arg->fType.columns() != c.fType.rows()) {
490 // merge scalars and smaller vectors together
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500491 if (!scalarCount) {
492 this->writeType(c.fType.componentType());
493 this->write(to_string(c.fType.rows()));
494 this->write("(");
495 }
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500496 scalarCount += arg->fType.columns();
Ethan Nicholas842d31b2019-01-22 10:59:11 -0500497 }
498 this->writeExpression(*arg, kSequence_Precedence);
499 if (scalarCount && scalarCount == c.fType.rows()) {
500 this->write(")");
501 scalarCount = 0;
502 }
503 }
504 this->write(")");
505 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400506}
507
508void MetalCodeGenerator::writeFragCoord() {
Ethan Nicholasf931e402019-07-26 15:40:33 -0400509 if (fRTHeightName.length()) {
510 this->write("float4(_fragCoord.x, ");
511 this->write(fRTHeightName.c_str());
512 this->write(" - _fragCoord.y, 0.0, _fragCoord.w)");
Jim Van Verth6bc650e2019-02-07 14:53:23 -0500513 } else {
514 this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
515 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400516}
517
518void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
519 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
520 case SK_FRAGCOLOR_BUILTIN:
Timothy Liang7d637782018-06-05 09:58:07 -0400521 this->write("_out->sk_FragColor");
Ethan Nicholascc305772017-10-13 16:17:45 -0400522 break;
Timothy Liang6403b0e2018-05-17 10:40:04 -0400523 case SK_FRAGCOORD_BUILTIN:
524 this->writeFragCoord();
525 break;
Timothy Liangdc89f192018-06-13 09:20:31 -0400526 case SK_VERTEXID_BUILTIN:
527 this->write("sk_VertexID");
528 break;
529 case SK_INSTANCEID_BUILTIN:
530 this->write("sk_InstanceID");
531 break;
Timothy Liang7b8875d2018-08-10 09:42:31 -0400532 case SK_CLOCKWISE_BUILTIN:
533 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
534 // clockwise to match Skia convention. This is also the default in MoltenVK.
535 this->write(fProgram.fSettings.fFlipY ? "_frontFacing" : "(!_frontFacing)");
536 break;
Ethan Nicholascc305772017-10-13 16:17:45 -0400537 default:
538 if (Variable::kGlobal_Storage == ref.fVariable.fStorage) {
539 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
540 this->write("_in.");
541 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400542 this->write("_out->");
Timothy Lianga06f2152018-05-24 15:33:31 -0400543 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
544 ref.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400545 this->write("_uniforms.");
546 } else {
Timothy Liangee84fe12018-05-18 14:38:19 -0400547 this->write("_globals->");
Ethan Nicholascc305772017-10-13 16:17:45 -0400548 }
549 }
Timothy Liang651286f2018-06-07 09:55:33 -0400550 this->writeName(ref.fVariable.fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400551 }
552}
553
554void MetalCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
555 this->writeExpression(*expr.fBase, kPostfix_Precedence);
556 this->write("[");
557 this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
558 this->write("]");
559}
560
561void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
Timothy Liang7d637782018-06-05 09:58:07 -0400562 const Type::Field* field = &f.fBase->fType.fields()[f.fFieldIndex];
Ethan Nicholascc305772017-10-13 16:17:45 -0400563 if (FieldAccess::kDefault_OwnerKind == f.fOwnerKind) {
564 this->writeExpression(*f.fBase, kPostfix_Precedence);
565 this->write(".");
566 }
Timothy Liang7d637782018-06-05 09:58:07 -0400567 switch (field->fModifiers.fLayout.fBuiltin) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400568 case SK_CLIPDISTANCE_BUILTIN:
569 this->write("gl_ClipDistance");
570 break;
571 case SK_POSITION_BUILTIN:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400572 this->write("_out->sk_Position");
Ethan Nicholascc305772017-10-13 16:17:45 -0400573 break;
574 default:
Timothy Liang7d637782018-06-05 09:58:07 -0400575 if (field->fName == "sk_PointSize") {
576 this->write("_out->sk_PointSize");
577 } else {
578 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
579 this->write("_globals->");
580 this->write(fInterfaceBlockNameMap[fInterfaceBlockMap[field]]);
581 this->write("->");
582 }
Timothy Liang651286f2018-06-07 09:55:33 -0400583 this->writeName(field->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400584 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400585 }
586}
587
588void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
Ethan Nicholas5476f2e2019-03-07 15:11:31 -0500589 int last = swizzle.fComponents.back();
590 if (last == SKSL_SWIZZLE_0 || last == SKSL_SWIZZLE_1) {
591 this->writeType(swizzle.fType);
592 this->write("(");
593 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400594 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
595 this->write(".");
596 for (int c : swizzle.fComponents) {
Ethan Nicholas5476f2e2019-03-07 15:11:31 -0500597 if (c >= 0) {
598 this->write(&("x\0y\0z\0w\0"[c * 2]));
599 }
600 }
601 if (last == SKSL_SWIZZLE_0) {
602 this->write(", 0)");
603 }
604 else if (last == SKSL_SWIZZLE_1) {
605 this->write(", 1)");
Ethan Nicholascc305772017-10-13 16:17:45 -0400606 }
607}
608
609MetalCodeGenerator::Precedence MetalCodeGenerator::GetBinaryPrecedence(Token::Kind op) {
610 switch (op) {
611 case Token::STAR: // fall through
612 case Token::SLASH: // fall through
613 case Token::PERCENT: return MetalCodeGenerator::kMultiplicative_Precedence;
614 case Token::PLUS: // fall through
615 case Token::MINUS: return MetalCodeGenerator::kAdditive_Precedence;
616 case Token::SHL: // fall through
617 case Token::SHR: return MetalCodeGenerator::kShift_Precedence;
618 case Token::LT: // fall through
619 case Token::GT: // fall through
620 case Token::LTEQ: // fall through
621 case Token::GTEQ: return MetalCodeGenerator::kRelational_Precedence;
622 case Token::EQEQ: // fall through
623 case Token::NEQ: return MetalCodeGenerator::kEquality_Precedence;
624 case Token::BITWISEAND: return MetalCodeGenerator::kBitwiseAnd_Precedence;
625 case Token::BITWISEXOR: return MetalCodeGenerator::kBitwiseXor_Precedence;
626 case Token::BITWISEOR: return MetalCodeGenerator::kBitwiseOr_Precedence;
627 case Token::LOGICALAND: return MetalCodeGenerator::kLogicalAnd_Precedence;
628 case Token::LOGICALXOR: return MetalCodeGenerator::kLogicalXor_Precedence;
629 case Token::LOGICALOR: return MetalCodeGenerator::kLogicalOr_Precedence;
630 case Token::EQ: // fall through
631 case Token::PLUSEQ: // fall through
632 case Token::MINUSEQ: // fall through
633 case Token::STAREQ: // fall through
634 case Token::SLASHEQ: // fall through
635 case Token::PERCENTEQ: // fall through
636 case Token::SHLEQ: // fall through
637 case Token::SHREQ: // fall through
638 case Token::LOGICALANDEQ: // fall through
639 case Token::LOGICALXOREQ: // fall through
640 case Token::LOGICALOREQ: // fall through
641 case Token::BITWISEANDEQ: // fall through
642 case Token::BITWISEXOREQ: // fall through
643 case Token::BITWISEOREQ: return MetalCodeGenerator::kAssignment_Precedence;
644 case Token::COMMA: return MetalCodeGenerator::kSequence_Precedence;
645 default: ABORT("unsupported binary operator");
646 }
647}
648
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500649void MetalCodeGenerator::writeMatrixTimesEqualHelper(const Type& left, const Type& right,
650 const Type& result) {
651 String key = "TimesEqual" + left.name() + right.name();
652 if (fHelpers.find(key) == fHelpers.end()) {
653 fExtraFunctions.printf("%s operator*=(thread %s& left, thread const %s& right) {\n"
654 " left = left * right;\n"
655 " return left;\n"
656 "}", result.name().c_str(), left.name().c_str(),
657 right.name().c_str());
658 }
659}
660
Ethan Nicholascc305772017-10-13 16:17:45 -0400661void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
662 Precedence parentPrecedence) {
663 Precedence precedence = GetBinaryPrecedence(b.fOperator);
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500664 bool needParens = precedence >= parentPrecedence;
665 switch (b.fOperator) {
666 case Token::EQEQ:
667 if (b.fLeft->fType.kind() == Type::kVector_Kind) {
668 this->write("all");
669 needParens = true;
670 }
671 break;
672 case Token::NEQ:
673 if (b.fLeft->fType.kind() == Type::kVector_Kind) {
Jim Van Verth36477b42019-04-11 14:57:30 -0400674 this->write("any");
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500675 needParens = true;
676 }
677 break;
678 default:
679 break;
680 }
681 if (needParens) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400682 this->write("(");
683 }
684 if (Compiler::IsAssignment(b.fOperator) &&
685 Expression::kVariableReference_Kind == b.fLeft->fKind &&
686 Variable::kParameter_Storage == ((VariableReference&) *b.fLeft).fVariable.fStorage &&
687 (((VariableReference&) *b.fLeft).fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) {
688 // writing to an out parameter. Since we have to turn those into pointers, we have to
689 // dereference it here.
690 this->write("*");
691 }
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500692 if (b.fOperator == Token::STAREQ && b.fLeft->fType.kind() == Type::kMatrix_Kind &&
693 b.fRight->fType.kind() == Type::kMatrix_Kind) {
694 this->writeMatrixTimesEqualHelper(b.fLeft->fType, b.fRight->fType, b.fType);
695 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400696 this->writeExpression(*b.fLeft, precedence);
697 if (b.fOperator != Token::EQ && Compiler::IsAssignment(b.fOperator) &&
698 Expression::kSwizzle_Kind == b.fLeft->fKind && !b.fLeft->hasSideEffects()) {
699 // This doesn't compile in Metal:
700 // float4 x = float4(1);
701 // x.xy *= float2x2(...);
702 // with the error message "non-const reference cannot bind to vector element",
703 // but switching it to x.xy = x.xy * float2x2(...) fixes it. We perform this tranformation
704 // as long as the LHS has no side effects, and hope for the best otherwise.
705 this->write(" = ");
706 this->writeExpression(*b.fLeft, kAssignment_Precedence);
707 this->write(" ");
708 String op = Compiler::OperatorName(b.fOperator);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400709 SkASSERT(op.endsWith("="));
Ethan Nicholascc305772017-10-13 16:17:45 -0400710 this->write(op.substr(0, op.size() - 1).c_str());
711 this->write(" ");
712 } else {
713 this->write(String(" ") + Compiler::OperatorName(b.fOperator) + " ");
714 }
715 this->writeExpression(*b.fRight, precedence);
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500716 if (needParens) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400717 this->write(")");
718 }
719}
720
721void MetalCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
722 Precedence parentPrecedence) {
723 if (kTernary_Precedence >= parentPrecedence) {
724 this->write("(");
725 }
726 this->writeExpression(*t.fTest, kTernary_Precedence);
727 this->write(" ? ");
728 this->writeExpression(*t.fIfTrue, kTernary_Precedence);
729 this->write(" : ");
730 this->writeExpression(*t.fIfFalse, kTernary_Precedence);
731 if (kTernary_Precedence >= parentPrecedence) {
732 this->write(")");
733 }
734}
735
736void MetalCodeGenerator::writePrefixExpression(const PrefixExpression& p,
737 Precedence parentPrecedence) {
738 if (kPrefix_Precedence >= parentPrecedence) {
739 this->write("(");
740 }
741 this->write(Compiler::OperatorName(p.fOperator));
742 this->writeExpression(*p.fOperand, kPrefix_Precedence);
743 if (kPrefix_Precedence >= parentPrecedence) {
744 this->write(")");
745 }
746}
747
748void MetalCodeGenerator::writePostfixExpression(const PostfixExpression& p,
749 Precedence parentPrecedence) {
750 if (kPostfix_Precedence >= parentPrecedence) {
751 this->write("(");
752 }
753 this->writeExpression(*p.fOperand, kPostfix_Precedence);
754 this->write(Compiler::OperatorName(p.fOperator));
755 if (kPostfix_Precedence >= parentPrecedence) {
756 this->write(")");
757 }
758}
759
760void MetalCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
761 this->write(b.fValue ? "true" : "false");
762}
763
764void MetalCodeGenerator::writeIntLiteral(const IntLiteral& i) {
765 if (i.fType == *fContext.fUInt_Type) {
766 this->write(to_string(i.fValue & 0xffffffff) + "u");
767 } else {
768 this->write(to_string((int32_t) i.fValue));
769 }
770}
771
772void MetalCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
773 this->write(to_string(f.fValue));
774}
775
776void MetalCodeGenerator::writeSetting(const Setting& s) {
777 ABORT("internal error; setting was not folded to a constant during compilation\n");
778}
779
780void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
Ethan Nicholasf931e402019-07-26 15:40:33 -0400781 fRTHeightName = fProgram.fInputs.fRTHeight ? "_globals->_anonInterface0->u_skRTHeight" : "";
Ethan Nicholascc305772017-10-13 16:17:45 -0400782 const char* separator = "";
783 if ("main" == f.fDeclaration.fName) {
784 switch (fProgram.fKind) {
785 case Program::kFragment_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400786#ifdef SK_MOLTENVK
787 this->write("fragment Outputs main0");
788#else
789 this->write("fragment Outputs fragmentMain");
790#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400791 break;
792 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400793#ifdef SK_MOLTENVK
Timothy Lianga06f2152018-05-24 15:33:31 -0400794 this->write("vertex Outputs main0");
Timothy Liangb8eeb802018-07-23 16:46:16 -0400795#else
796 this->write("vertex Outputs vertexMain");
797#endif
Ethan Nicholascc305772017-10-13 16:17:45 -0400798 break;
799 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400800 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400801 }
802 this->write("(Inputs _in [[stage_in]]");
803 if (-1 != fUniformBuffer) {
804 this->write(", constant Uniforms& _uniforms [[buffer(" +
805 to_string(fUniformBuffer) + ")]]");
806 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400807 for (const auto& e : fProgram) {
808 if (ProgramElement::kVar_Kind == e.fKind) {
809 VarDeclarations& decls = (VarDeclarations&) e;
810 if (!decls.fVars.size()) {
811 continue;
812 }
Timothy Liangee84fe12018-05-18 14:38:19 -0400813 for (const auto& stmt: decls.fVars) {
Timothy Liang6403b0e2018-05-17 10:40:04 -0400814 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -0400815 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
Timothy Liang7d637782018-06-05 09:58:07 -0400816 this->write(", texture2d<float> "); // FIXME - support other texture types
Timothy Liang651286f2018-06-07 09:55:33 -0400817 this->writeName(var.fVar->fName);
Timothy Liangee84fe12018-05-18 14:38:19 -0400818 this->write("[[texture(");
Timothy Lianga06f2152018-05-24 15:33:31 -0400819 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
820 this->write(")]]");
821 this->write(", sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -0400822 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400823 this->write(SAMPLER_SUFFIX);
824 this->write("[[sampler(");
825 this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
Timothy Liangee84fe12018-05-18 14:38:19 -0400826 this->write(")]]");
827 }
Timothy Liang6403b0e2018-05-17 10:40:04 -0400828 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400829 } else if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
830 InterfaceBlock& intf = (InterfaceBlock&) e;
831 if ("sk_PerVertex" == intf.fTypeName) {
832 continue;
833 }
834 this->write(", constant ");
835 this->writeType(intf.fVariable.fType);
836 this->write("& " );
837 this->write(fInterfaceBlockNameMap[&intf]);
838 this->write(" [[buffer(");
Timothy Liang057c3902018-08-08 10:48:45 -0400839#ifdef SK_MOLTENVK
Timothy Liang7d637782018-06-05 09:58:07 -0400840 this->write(to_string(intf.fVariable.fModifiers.fLayout.fSet));
Timothy Liang057c3902018-08-08 10:48:45 -0400841#else
842 this->write(to_string(intf.fVariable.fModifiers.fLayout.fBinding));
843#endif
Timothy Lianga06f2152018-05-24 15:33:31 -0400844 this->write(")]]");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400845 }
846 }
Jim Van Verth6bc650e2019-02-07 14:53:23 -0500847 if (fProgram.fKind == Program::kFragment_Kind) {
848 if (fProgram.fInputs.fRTHeight && fInterfaceBlockNameMap.empty()) {
Timothy Liang5422f9a2018-08-10 10:57:55 -0400849#ifdef SK_MOLTENVK
850 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(0)]]");
851#else
852 this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
853#endif
Ethan Nicholasf931e402019-07-26 15:40:33 -0400854 fRTHeightName = "_anonInterface0.u_skRTHeight";
Timothy Liang5422f9a2018-08-10 10:57:55 -0400855 }
Timothy Liang7b8875d2018-08-10 09:42:31 -0400856 this->write(", bool _frontFacing [[front_facing]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400857 this->write(", float4 _fragCoord [[position]]");
Timothy Liangdc89f192018-06-13 09:20:31 -0400858 } else if (fProgram.fKind == Program::kVertex_Kind) {
859 this->write(", uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
Timothy Liang7d637782018-06-05 09:58:07 -0400860 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400861 separator = ", ";
862 } else {
863 this->writeType(f.fDeclaration.fReturnType);
Timothy Liang651286f2018-06-07 09:55:33 -0400864 this->write(" ");
865 this->writeName(f.fDeclaration.fName);
866 this->write("(");
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400867 Requirements requirements = this->requirements(f.fDeclaration);
868 if (requirements & kInputs_Requirement) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400869 this->write("Inputs _in");
870 separator = ", ";
871 }
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400872 if (requirements & kOutputs_Requirement) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400873 this->write(separator);
Timothy Liangee84fe12018-05-18 14:38:19 -0400874 this->write("thread Outputs* _out");
Ethan Nicholascc305772017-10-13 16:17:45 -0400875 separator = ", ";
876 }
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400877 if (requirements & kUniforms_Requirement) {
Ethan Nicholascc305772017-10-13 16:17:45 -0400878 this->write(separator);
879 this->write("Uniforms _uniforms");
880 separator = ", ";
881 }
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400882 if (requirements & kGlobals_Requirement) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400883 this->write(separator);
884 this->write("thread Globals* _globals");
885 separator = ", ";
886 }
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400887 if (requirements & kFragCoord_Requirement) {
888 this->write(separator);
889 this->write("float4 _fragCoord");
890 separator = ", ";
891 }
Ethan Nicholascc305772017-10-13 16:17:45 -0400892 }
893 for (const auto& param : f.fDeclaration.fParameters) {
894 this->write(separator);
895 separator = ", ";
896 this->writeModifiers(param->fModifiers, false);
897 std::vector<int> sizes;
898 const Type* type = &param->fType;
899 while (Type::kArray_Kind == type->kind()) {
900 sizes.push_back(type->columns());
901 type = &type->componentType();
902 }
903 this->writeType(*type);
904 if (param->fModifiers.fFlags & Modifiers::kOut_Flag) {
905 this->write("*");
906 }
Timothy Liang651286f2018-06-07 09:55:33 -0400907 this->write(" ");
908 this->writeName(param->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -0400909 for (int s : sizes) {
910 if (s <= 0) {
911 this->write("[]");
912 } else {
913 this->write("[" + to_string(s) + "]");
914 }
915 }
916 }
917 this->writeLine(") {");
918
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400919 SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
Brian Salomondc092132018-04-04 10:14:16 -0400920
Ethan Nicholascc305772017-10-13 16:17:45 -0400921 if ("main" == f.fDeclaration.fName) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400922 if (fNeedsGlobalStructInit) {
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500923 this->writeLine(" Globals globalStruct{");
924 const char* separator = "";
Timothy Liang7d637782018-06-05 09:58:07 -0400925 for (const auto& intf: fInterfaceBlockNameMap) {
926 const auto& intfName = intf.second;
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500927 this->write(separator);
928 separator = ", ";
929 this->write("&");
Timothy Liang651286f2018-06-07 09:55:33 -0400930 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -0400931 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400932 for (const auto& var: fInitNonConstGlobalVars) {
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500933 this->write(separator);
934 separator = ", ";
Timothy Liangee84fe12018-05-18 14:38:19 -0400935 this->writeVarInitializer(*var->fVar, *var->fValue);
Timothy Liangee84fe12018-05-18 14:38:19 -0400936 }
Timothy Lianga06f2152018-05-24 15:33:31 -0400937 for (const auto& texture: fTextures) {
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500938 this->write(separator);
939 separator = ", ";
Timothy Liang651286f2018-06-07 09:55:33 -0400940 this->writeName(texture->fName);
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500941 this->write(separator);
Timothy Liang651286f2018-06-07 09:55:33 -0400942 this->writeName(texture->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -0400943 this->write(SAMPLER_SUFFIX);
Timothy Liangee84fe12018-05-18 14:38:19 -0400944 }
Ethan Nicholasb47eb182019-12-20 10:49:41 -0500945 this->writeLine("};");
946 this->writeLine(" thread Globals* _globals = &globalStruct;");
947 this->writeLine(" (void)_globals;");
Timothy Liang6403b0e2018-05-17 10:40:04 -0400948 }
Timothy Liang7d637782018-06-05 09:58:07 -0400949 this->writeLine(" Outputs _outputStruct;");
950 this->writeLine(" thread Outputs* _out = &_outputStruct;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400951 }
952 fFunctionHeader = "";
953 OutputStream* oldOut = fOut;
954 StringStream buffer;
955 fOut = &buffer;
956 fIndentation++;
957 this->writeStatements(((Block&) *f.fBody).fStatements);
958 if ("main" == f.fDeclaration.fName) {
959 switch (fProgram.fKind) {
960 case Program::kFragment_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -0400961 this->writeLine("return *_out;");
Ethan Nicholascc305772017-10-13 16:17:45 -0400962 break;
963 case Program::kVertex_Kind:
Timothy Liangb8eeb802018-07-23 16:46:16 -0400964 this->writeLine("_out->sk_Position.y = -_out->sk_Position.y;");
Timothy Lianga06f2152018-05-24 15:33:31 -0400965 this->writeLine("return *_out;"); // FIXME - detect if function already has return
Ethan Nicholascc305772017-10-13 16:17:45 -0400966 break;
967 default:
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400968 SkASSERT(false);
Ethan Nicholascc305772017-10-13 16:17:45 -0400969 }
970 }
971 fIndentation--;
972 this->writeLine("}");
973
974 fOut = oldOut;
975 this->write(fFunctionHeader);
976 this->write(buffer.str());
977}
978
979void MetalCodeGenerator::writeModifiers(const Modifiers& modifiers,
980 bool globalContext) {
981 if (modifiers.fFlags & Modifiers::kOut_Flag) {
982 this->write("thread ");
983 }
984 if (modifiers.fFlags & Modifiers::kConst_Flag) {
Timothy Liangee84fe12018-05-18 14:38:19 -0400985 this->write("constant ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400986 }
987}
988
989void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
990 if ("sk_PerVertex" == intf.fTypeName) {
991 return;
992 }
993 this->writeModifiers(intf.fVariable.fModifiers, true);
Timothy Liangdc89f192018-06-13 09:20:31 -0400994 this->write("struct ");
Ethan Nicholascc305772017-10-13 16:17:45 -0400995 this->writeLine(intf.fTypeName + " {");
Ethan Nicholascc305772017-10-13 16:17:45 -0400996 const Type* structType = &intf.fVariable.fType;
Timothy Lianga06f2152018-05-24 15:33:31 -0400997 fWrittenStructs.push_back(structType);
Ethan Nicholascc305772017-10-13 16:17:45 -0400998 while (Type::kArray_Kind == structType->kind()) {
999 structType = &structType->componentType();
1000 }
Timothy Liangdc89f192018-06-13 09:20:31 -04001001 fIndentation++;
1002 writeFields(structType->fields(), structType->fOffset, &intf);
Jim Van Verth3d482992019-02-07 10:48:05 -05001003 if (fProgram.fInputs.fRTHeight) {
Timothy Liang7d637782018-06-05 09:58:07 -04001004 this->writeLine("float u_skRTHeight;");
Ethan Nicholascc305772017-10-13 16:17:45 -04001005 }
1006 fIndentation--;
1007 this->write("}");
1008 if (intf.fInstanceName.size()) {
1009 this->write(" ");
1010 this->write(intf.fInstanceName);
1011 for (const auto& size : intf.fSizes) {
1012 this->write("[");
1013 if (size) {
1014 this->writeExpression(*size, kTopLevel_Precedence);
1015 }
1016 this->write("]");
1017 }
Timothy Lianga06f2152018-05-24 15:33:31 -04001018 fInterfaceBlockNameMap[&intf] = intf.fInstanceName;
1019 } else {
Timothy Liang7d637782018-06-05 09:58:07 -04001020 fInterfaceBlockNameMap[&intf] = "_anonInterface" + to_string(fAnonInterfaceCount++);
Ethan Nicholascc305772017-10-13 16:17:45 -04001021 }
1022 this->writeLine(";");
1023}
1024
Timothy Liangdc89f192018-06-13 09:20:31 -04001025void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int parentOffset,
1026 const InterfaceBlock* parentIntf) {
Timothy Liang609fbe32018-08-10 16:40:49 -04001027#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -04001028 MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
Timothy Liang609fbe32018-08-10 16:40:49 -04001029#else
1030 MemoryLayout memoryLayout(MemoryLayout::kMetal_Standard);
1031#endif
Timothy Liangdc89f192018-06-13 09:20:31 -04001032 int currentOffset = 0;
1033 for (const auto& field: fields) {
1034 int fieldOffset = field.fModifiers.fLayout.fOffset;
1035 const Type* fieldType = field.fType;
1036 if (fieldOffset != -1) {
1037 if (currentOffset > fieldOffset) {
1038 fErrors.error(parentOffset,
1039 "offset of field '" + field.fName + "' must be at least " +
1040 to_string((int) currentOffset));
1041 } else if (currentOffset < fieldOffset) {
1042 this->write("char pad");
1043 this->write(to_string(fPaddingCount++));
1044 this->write("[");
1045 this->write(to_string(fieldOffset - currentOffset));
1046 this->writeLine("];");
1047 currentOffset = fieldOffset;
1048 }
1049 int alignment = memoryLayout.alignment(*fieldType);
1050 if (fieldOffset % alignment) {
1051 fErrors.error(parentOffset,
1052 "offset of field '" + field.fName + "' must be a multiple of " +
1053 to_string((int) alignment));
1054 }
1055 }
Timothy Liang609fbe32018-08-10 16:40:49 -04001056#ifdef SK_MOLTENVK
Timothy Liangdc89f192018-06-13 09:20:31 -04001057 if (fieldType->kind() == Type::kVector_Kind &&
1058 fieldType->columns() == 3) {
Timothy Liang609fbe32018-08-10 16:40:49 -04001059 SkASSERT(memoryLayout.size(*fieldType) == 3);
Timothy Liangdc89f192018-06-13 09:20:31 -04001060 // Pack all vec3 types so that their size in bytes will match what was expected in the
1061 // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
1062 // has vec3 equal to 3 * component type.
Timothy Liang609fbe32018-08-10 16:40:49 -04001063
1064 // FIXME - Packed vectors can't be accessed by swizzles, but can be indexed into. A
1065 // combination of this being a problem which only occurs when using MoltenVK and the
1066 // fact that we haven't swizzled a vec3 yet means that this problem hasn't been
1067 // addressed.
Timothy Liangdc89f192018-06-13 09:20:31 -04001068 this->write(PACKED_PREFIX);
1069 }
Timothy Liang609fbe32018-08-10 16:40:49 -04001070#endif
Timothy Liangdc89f192018-06-13 09:20:31 -04001071 currentOffset += memoryLayout.size(*fieldType);
1072 std::vector<int> sizes;
1073 while (fieldType->kind() == Type::kArray_Kind) {
1074 sizes.push_back(fieldType->columns());
1075 fieldType = &fieldType->componentType();
1076 }
1077 this->writeModifiers(field.fModifiers, false);
1078 this->writeType(*fieldType);
1079 this->write(" ");
1080 this->writeName(field.fName);
1081 for (int s : sizes) {
1082 if (s <= 0) {
1083 this->write("[]");
1084 } else {
1085 this->write("[" + to_string(s) + "]");
1086 }
1087 }
1088 this->writeLine(";");
1089 if (parentIntf) {
1090 fInterfaceBlockMap[&field] = parentIntf;
1091 }
1092 }
1093}
1094
Ethan Nicholascc305772017-10-13 16:17:45 -04001095void MetalCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
1096 this->writeExpression(value, kTopLevel_Precedence);
1097}
1098
Timothy Liang651286f2018-06-07 09:55:33 -04001099void MetalCodeGenerator::writeName(const String& name) {
1100 if (fReservedWords.find(name) != fReservedWords.end()) {
1101 this->write("_"); // adding underscore before name to avoid conflict with reserved words
1102 }
1103 this->write(name);
1104}
1105
Ethan Nicholascc305772017-10-13 16:17:45 -04001106void MetalCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001107 SkASSERT(decl.fVars.size() > 0);
Ethan Nicholascc305772017-10-13 16:17:45 -04001108 bool wroteType = false;
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001109 for (const auto& stmt : decl.fVars) {
1110 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liangee84fe12018-05-18 14:38:19 -04001111 if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001112 continue;
1113 }
1114 if (wroteType) {
1115 this->write(", ");
1116 } else {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001117 this->writeModifiers(var.fVar->fModifiers, global);
Ethan Nicholascc305772017-10-13 16:17:45 -04001118 this->writeType(decl.fBaseType);
1119 this->write(" ");
1120 wroteType = true;
1121 }
Timothy Liang651286f2018-06-07 09:55:33 -04001122 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001123 for (const auto& size : var.fSizes) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001124 this->write("[");
1125 if (size) {
1126 this->writeExpression(*size, kTopLevel_Precedence);
1127 }
1128 this->write("]");
1129 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001130 if (var.fValue) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001131 this->write(" = ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001132 this->writeVarInitializer(*var.fVar, *var.fValue);
Ethan Nicholascc305772017-10-13 16:17:45 -04001133 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001134 }
1135 if (wroteType) {
1136 this->write(";");
1137 }
1138}
1139
1140void MetalCodeGenerator::writeStatement(const Statement& s) {
1141 switch (s.fKind) {
1142 case Statement::kBlock_Kind:
1143 this->writeBlock((Block&) s);
1144 break;
1145 case Statement::kExpression_Kind:
1146 this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
1147 this->write(";");
1148 break;
1149 case Statement::kReturn_Kind:
1150 this->writeReturnStatement((ReturnStatement&) s);
1151 break;
1152 case Statement::kVarDeclarations_Kind:
1153 this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
1154 break;
1155 case Statement::kIf_Kind:
1156 this->writeIfStatement((IfStatement&) s);
1157 break;
1158 case Statement::kFor_Kind:
1159 this->writeForStatement((ForStatement&) s);
1160 break;
1161 case Statement::kWhile_Kind:
1162 this->writeWhileStatement((WhileStatement&) s);
1163 break;
1164 case Statement::kDo_Kind:
1165 this->writeDoStatement((DoStatement&) s);
1166 break;
1167 case Statement::kSwitch_Kind:
1168 this->writeSwitchStatement((SwitchStatement&) s);
1169 break;
1170 case Statement::kBreak_Kind:
1171 this->write("break;");
1172 break;
1173 case Statement::kContinue_Kind:
1174 this->write("continue;");
1175 break;
1176 case Statement::kDiscard_Kind:
Timothy Lianga06f2152018-05-24 15:33:31 -04001177 this->write("discard_fragment();");
Ethan Nicholascc305772017-10-13 16:17:45 -04001178 break;
1179 case Statement::kNop_Kind:
1180 this->write(";");
1181 break;
1182 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001183#ifdef SK_DEBUG
Ethan Nicholascc305772017-10-13 16:17:45 -04001184 ABORT("unsupported statement: %s", s.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001185#endif
1186 break;
Ethan Nicholascc305772017-10-13 16:17:45 -04001187 }
1188}
1189
1190void MetalCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
1191 for (const auto& s : statements) {
1192 if (!s->isEmpty()) {
1193 this->writeStatement(*s);
1194 this->writeLine();
1195 }
1196 }
1197}
1198
1199void MetalCodeGenerator::writeBlock(const Block& b) {
1200 this->writeLine("{");
1201 fIndentation++;
1202 this->writeStatements(b.fStatements);
1203 fIndentation--;
1204 this->write("}");
1205}
1206
1207void MetalCodeGenerator::writeIfStatement(const IfStatement& stmt) {
1208 this->write("if (");
1209 this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
1210 this->write(") ");
1211 this->writeStatement(*stmt.fIfTrue);
1212 if (stmt.fIfFalse) {
1213 this->write(" else ");
1214 this->writeStatement(*stmt.fIfFalse);
1215 }
1216}
1217
1218void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
1219 this->write("for (");
1220 if (f.fInitializer && !f.fInitializer->isEmpty()) {
1221 this->writeStatement(*f.fInitializer);
1222 } else {
1223 this->write("; ");
1224 }
1225 if (f.fTest) {
1226 this->writeExpression(*f.fTest, kTopLevel_Precedence);
1227 }
1228 this->write("; ");
1229 if (f.fNext) {
1230 this->writeExpression(*f.fNext, kTopLevel_Precedence);
1231 }
1232 this->write(") ");
1233 this->writeStatement(*f.fStatement);
1234}
1235
1236void MetalCodeGenerator::writeWhileStatement(const WhileStatement& w) {
1237 this->write("while (");
1238 this->writeExpression(*w.fTest, kTopLevel_Precedence);
1239 this->write(") ");
1240 this->writeStatement(*w.fStatement);
1241}
1242
1243void MetalCodeGenerator::writeDoStatement(const DoStatement& d) {
1244 this->write("do ");
1245 this->writeStatement(*d.fStatement);
1246 this->write(" while (");
1247 this->writeExpression(*d.fTest, kTopLevel_Precedence);
1248 this->write(");");
1249}
1250
1251void MetalCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
1252 this->write("switch (");
1253 this->writeExpression(*s.fValue, kTopLevel_Precedence);
1254 this->writeLine(") {");
1255 fIndentation++;
1256 for (const auto& c : s.fCases) {
1257 if (c->fValue) {
1258 this->write("case ");
1259 this->writeExpression(*c->fValue, kTopLevel_Precedence);
1260 this->writeLine(":");
1261 } else {
1262 this->writeLine("default:");
1263 }
1264 fIndentation++;
1265 for (const auto& stmt : c->fStatements) {
1266 this->writeStatement(*stmt);
1267 this->writeLine();
1268 }
1269 fIndentation--;
1270 }
1271 fIndentation--;
1272 this->write("}");
1273}
1274
1275void MetalCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
1276 this->write("return");
1277 if (r.fExpression) {
1278 this->write(" ");
1279 this->writeExpression(*r.fExpression, kTopLevel_Precedence);
1280 }
1281 this->write(";");
1282}
1283
1284void MetalCodeGenerator::writeHeader() {
1285 this->write("#include <metal_stdlib>\n");
1286 this->write("#include <simd/simd.h>\n");
1287 this->write("using namespace metal;\n");
1288}
1289
1290void MetalCodeGenerator::writeUniformStruct() {
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001291 for (const auto& e : fProgram) {
1292 if (ProgramElement::kVar_Kind == e.fKind) {
1293 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001294 if (!decls.fVars.size()) {
1295 continue;
1296 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001297 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001298 if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1299 first.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001300 if (-1 == fUniformBuffer) {
1301 this->write("struct Uniforms {\n");
1302 fUniformBuffer = first.fModifiers.fLayout.fSet;
1303 if (-1 == fUniformBuffer) {
1304 fErrors.error(decls.fOffset, "Metal uniforms must have 'layout(set=...)'");
1305 }
1306 } else if (first.fModifiers.fLayout.fSet != fUniformBuffer) {
1307 if (-1 == fUniformBuffer) {
1308 fErrors.error(decls.fOffset, "Metal backend requires all uniforms to have "
1309 "the same 'layout(set=...)'");
1310 }
1311 }
1312 this->write(" ");
1313 this->writeType(first.fType);
1314 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001315 for (const auto& stmt : decls.fVars) {
1316 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001317 this->writeName(var.fVar->fName);
Ethan Nicholascc305772017-10-13 16:17:45 -04001318 }
1319 this->write(";\n");
1320 }
1321 }
1322 }
1323 if (-1 != fUniformBuffer) {
1324 this->write("};\n");
1325 }
1326}
1327
1328void MetalCodeGenerator::writeInputStruct() {
1329 this->write("struct Inputs {\n");
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001330 for (const auto& e : fProgram) {
1331 if (ProgramElement::kVar_Kind == e.fKind) {
1332 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001333 if (!decls.fVars.size()) {
1334 continue;
1335 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001336 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001337 if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
1338 -1 == first.fModifiers.fLayout.fBuiltin) {
1339 this->write(" ");
1340 this->writeType(first.fType);
1341 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001342 for (const auto& stmt : decls.fVars) {
1343 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001344 this->writeName(var.fVar->fName);
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001345 if (-1 != var.fVar->fModifiers.fLayout.fLocation) {
Timothy Liang7d637782018-06-05 09:58:07 -04001346 if (fProgram.fKind == Program::kVertex_Kind) {
1347 this->write(" [[attribute(" +
1348 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1349 } else if (fProgram.fKind == Program::kFragment_Kind) {
1350 this->write(" [[user(locn" +
1351 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1352 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001353 }
1354 }
1355 this->write(";\n");
1356 }
1357 }
1358 }
1359 this->write("};\n");
1360}
1361
1362void MetalCodeGenerator::writeOutputStruct() {
1363 this->write("struct Outputs {\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001364 if (fProgram.fKind == Program::kVertex_Kind) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001365 this->write(" float4 sk_Position [[position]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001366 } else if (fProgram.fKind == Program::kFragment_Kind) {
Timothy Liangde0be802018-08-10 13:48:08 -04001367 this->write(" float4 sk_FragColor [[color(0)]];\n");
Timothy Liang7d637782018-06-05 09:58:07 -04001368 }
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001369 for (const auto& e : fProgram) {
1370 if (ProgramElement::kVar_Kind == e.fKind) {
1371 VarDeclarations& decls = (VarDeclarations&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001372 if (!decls.fVars.size()) {
1373 continue;
1374 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001375 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholascc305772017-10-13 16:17:45 -04001376 if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
1377 -1 == first.fModifiers.fLayout.fBuiltin) {
1378 this->write(" ");
1379 this->writeType(first.fType);
1380 this->write(" ");
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001381 for (const auto& stmt : decls.fVars) {
1382 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001383 this->writeName(var.fVar->fName);
Timothy Liang7d637782018-06-05 09:58:07 -04001384 if (fProgram.fKind == Program::kVertex_Kind) {
1385 this->write(" [[user(locn" +
1386 to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
1387 } else if (fProgram.fKind == Program::kFragment_Kind) {
1388 this->write(" [[color(" +
Timothy Liangde0be802018-08-10 13:48:08 -04001389 to_string(var.fVar->fModifiers.fLayout.fLocation) +")");
1390 int colorIndex = var.fVar->fModifiers.fLayout.fIndex;
1391 if (colorIndex) {
1392 this->write(", index(" + to_string(colorIndex) + ")");
1393 }
1394 this->write("]]");
Timothy Liang7d637782018-06-05 09:58:07 -04001395 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001396 }
1397 this->write(";\n");
1398 }
1399 }
Timothy Liang7d637782018-06-05 09:58:07 -04001400 }
1401 if (fProgram.fKind == Program::kVertex_Kind) {
1402 this->write(" float sk_PointSize;\n");
1403 }
1404 this->write("};\n");
1405}
1406
1407void MetalCodeGenerator::writeInterfaceBlocks() {
1408 bool wroteInterfaceBlock = false;
1409 for (const auto& e : fProgram) {
1410 if (ProgramElement::kInterfaceBlock_Kind == e.fKind) {
1411 this->writeInterfaceBlock((InterfaceBlock&) e);
1412 wroteInterfaceBlock = true;
1413 }
1414 }
Jim Van Verth3d482992019-02-07 10:48:05 -05001415 if (!wroteInterfaceBlock && fProgram.fInputs.fRTHeight) {
Timothy Liang7d637782018-06-05 09:58:07 -04001416 this->writeLine("struct sksl_synthetic_uniforms {");
1417 this->writeLine(" float u_skRTHeight;");
1418 this->writeLine("};");
1419 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001420}
1421
Timothy Liangee84fe12018-05-18 14:38:19 -04001422void MetalCodeGenerator::writeGlobalStruct() {
1423 bool wroteStructDecl = false;
Timothy Liang7d637782018-06-05 09:58:07 -04001424 for (const auto& intf : fInterfaceBlockNameMap) {
1425 if (!wroteStructDecl) {
1426 this->write("struct Globals {\n");
1427 wroteStructDecl = true;
1428 }
1429 fNeedsGlobalStructInit = true;
1430 const auto& intfType = intf.first;
1431 const auto& intfName = intf.second;
1432 this->write(" constant ");
1433 this->write(intfType->fTypeName);
1434 this->write("* ");
Timothy Liang651286f2018-06-07 09:55:33 -04001435 this->writeName(intfName);
Timothy Liang7d637782018-06-05 09:58:07 -04001436 this->write(";\n");
1437 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001438 for (const auto& e : fProgram) {
1439 if (ProgramElement::kVar_Kind == e.fKind) {
1440 VarDeclarations& decls = (VarDeclarations&) e;
1441 if (!decls.fVars.size()) {
1442 continue;
1443 }
1444 const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
Timothy Lianga06f2152018-05-24 15:33:31 -04001445 if ((!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) ||
1446 first.fType.kind() == Type::kSampler_Kind) {
Timothy Liangee84fe12018-05-18 14:38:19 -04001447 if (!wroteStructDecl) {
1448 this->write("struct Globals {\n");
1449 wroteStructDecl = true;
1450 }
1451 fNeedsGlobalStructInit = true;
1452 this->write(" ");
1453 this->writeType(first.fType);
1454 this->write(" ");
1455 for (const auto& stmt : decls.fVars) {
1456 VarDeclaration& var = (VarDeclaration&) *stmt;
Timothy Liang651286f2018-06-07 09:55:33 -04001457 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001458 if (var.fVar->fType.kind() == Type::kSampler_Kind) {
1459 fTextures.push_back(var.fVar);
1460 this->write(";\n");
1461 this->write(" sampler ");
Timothy Liang651286f2018-06-07 09:55:33 -04001462 this->writeName(var.fVar->fName);
Timothy Lianga06f2152018-05-24 15:33:31 -04001463 this->write(SAMPLER_SUFFIX);
1464 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001465 if (var.fValue) {
1466 fInitNonConstGlobalVars.push_back(&var);
1467 }
1468 }
1469 this->write(";\n");
1470 }
1471 }
1472 }
Timothy Liangee84fe12018-05-18 14:38:19 -04001473 if (wroteStructDecl) {
1474 this->write("};\n");
1475 }
1476}
1477
Ethan Nicholascc305772017-10-13 16:17:45 -04001478void MetalCodeGenerator::writeProgramElement(const ProgramElement& e) {
1479 switch (e.fKind) {
1480 case ProgramElement::kExtension_Kind:
1481 break;
1482 case ProgramElement::kVar_Kind: {
1483 VarDeclarations& decl = (VarDeclarations&) e;
1484 if (decl.fVars.size() > 0) {
Ethan Nicholas82a62d22017-11-07 14:42:10 +00001485 int builtin = ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
Ethan Nicholascc305772017-10-13 16:17:45 -04001486 if (-1 == builtin) {
1487 // normal var
1488 this->writeVarDeclarations(decl, true);
1489 this->writeLine();
1490 } else if (SK_FRAGCOLOR_BUILTIN == builtin) {
1491 // ignore
1492 }
1493 }
1494 break;
1495 }
1496 case ProgramElement::kInterfaceBlock_Kind:
Timothy Liang7d637782018-06-05 09:58:07 -04001497 // handled in writeInterfaceBlocks, do nothing
Ethan Nicholascc305772017-10-13 16:17:45 -04001498 break;
1499 case ProgramElement::kFunction_Kind:
1500 this->writeFunction((FunctionDefinition&) e);
1501 break;
1502 case ProgramElement::kModifiers_Kind:
1503 this->writeModifiers(((ModifiersDeclaration&) e).fModifiers, true);
1504 this->writeLine(";");
1505 break;
1506 default:
Ethan Nicholas2a099da2020-01-02 14:40:54 -05001507#ifdef SK_DEBUG
1508 ABORT("unsupported program element: %s\n", e.description().c_str());
1509#endif
1510 break;
Ethan Nicholascc305772017-10-13 16:17:45 -04001511 }
1512}
1513
1514MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Expression& e) {
1515 switch (e.fKind) {
1516 case Expression::kFunctionCall_Kind: {
1517 const FunctionCall& f = (const FunctionCall&) e;
1518 Requirements result = this->requirements(f.fFunction);
1519 for (const auto& e : f.fArguments) {
1520 result |= this->requirements(*e);
1521 }
1522 return result;
1523 }
1524 case Expression::kConstructor_Kind: {
1525 const Constructor& c = (const Constructor&) e;
1526 Requirements result = kNo_Requirements;
1527 for (const auto& e : c.fArguments) {
1528 result |= this->requirements(*e);
1529 }
1530 return result;
1531 }
Timothy Liang7d637782018-06-05 09:58:07 -04001532 case Expression::kFieldAccess_Kind: {
1533 const FieldAccess& f = (const FieldAccess&) e;
1534 if (FieldAccess::kAnonymousInterfaceBlock_OwnerKind == f.fOwnerKind) {
1535 return kGlobals_Requirement;
1536 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001537 return this->requirements(*((const FieldAccess&) e).fBase);
Timothy Liang7d637782018-06-05 09:58:07 -04001538 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001539 case Expression::kSwizzle_Kind:
1540 return this->requirements(*((const Swizzle&) e).fBase);
1541 case Expression::kBinary_Kind: {
1542 const BinaryExpression& b = (const BinaryExpression&) e;
1543 return this->requirements(*b.fLeft) | this->requirements(*b.fRight);
1544 }
1545 case Expression::kIndex_Kind: {
1546 const IndexExpression& idx = (const IndexExpression&) e;
1547 return this->requirements(*idx.fBase) | this->requirements(*idx.fIndex);
1548 }
1549 case Expression::kPrefix_Kind:
1550 return this->requirements(*((const PrefixExpression&) e).fOperand);
1551 case Expression::kPostfix_Kind:
1552 return this->requirements(*((const PostfixExpression&) e).fOperand);
1553 case Expression::kTernary_Kind: {
1554 const TernaryExpression& t = (const TernaryExpression&) e;
1555 return this->requirements(*t.fTest) | this->requirements(*t.fIfTrue) |
1556 this->requirements(*t.fIfFalse);
1557 }
1558 case Expression::kVariableReference_Kind: {
1559 const VariableReference& v = (const VariableReference&) e;
1560 Requirements result = kNo_Requirements;
1561 if (v.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -04001562 result = kGlobals_Requirement | kFragCoord_Requirement;
Ethan Nicholascc305772017-10-13 16:17:45 -04001563 } else if (Variable::kGlobal_Storage == v.fVariable.fStorage) {
1564 if (v.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
1565 result = kInputs_Requirement;
1566 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
1567 result = kOutputs_Requirement;
Timothy Lianga06f2152018-05-24 15:33:31 -04001568 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
1569 v.fVariable.fType.kind() != Type::kSampler_Kind) {
Ethan Nicholascc305772017-10-13 16:17:45 -04001570 result = kUniforms_Requirement;
Timothy Liangee84fe12018-05-18 14:38:19 -04001571 } else {
1572 result = kGlobals_Requirement;
Ethan Nicholascc305772017-10-13 16:17:45 -04001573 }
1574 }
1575 return result;
1576 }
1577 default:
1578 return kNo_Requirements;
1579 }
1580}
1581
1582MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Statement& s) {
1583 switch (s.fKind) {
1584 case Statement::kBlock_Kind: {
1585 Requirements result = kNo_Requirements;
1586 for (const auto& child : ((const Block&) s).fStatements) {
1587 result |= this->requirements(*child);
1588 }
1589 return result;
1590 }
Timothy Liang7d637782018-06-05 09:58:07 -04001591 case Statement::kVarDeclaration_Kind: {
1592 Requirements result = kNo_Requirements;
1593 const VarDeclaration& var = (const VarDeclaration&) s;
1594 if (var.fValue) {
1595 result = this->requirements(*var.fValue);
1596 }
1597 return result;
1598 }
1599 case Statement::kVarDeclarations_Kind: {
1600 Requirements result = kNo_Requirements;
1601 const VarDeclarations& decls = *((const VarDeclarationsStatement&) s).fDeclaration;
1602 for (const auto& stmt : decls.fVars) {
1603 result |= this->requirements(*stmt);
1604 }
1605 return result;
1606 }
Ethan Nicholascc305772017-10-13 16:17:45 -04001607 case Statement::kExpression_Kind:
1608 return this->requirements(*((const ExpressionStatement&) s).fExpression);
1609 case Statement::kReturn_Kind: {
1610 const ReturnStatement& r = (const ReturnStatement&) s;
1611 if (r.fExpression) {
1612 return this->requirements(*r.fExpression);
1613 }
1614 return kNo_Requirements;
1615 }
1616 case Statement::kIf_Kind: {
1617 const IfStatement& i = (const IfStatement&) s;
1618 return this->requirements(*i.fTest) |
1619 this->requirements(*i.fIfTrue) |
Ethan Nicholasf931e402019-07-26 15:40:33 -04001620 (i.fIfFalse ? this->requirements(*i.fIfFalse) : 0);
Ethan Nicholascc305772017-10-13 16:17:45 -04001621 }
1622 case Statement::kFor_Kind: {
1623 const ForStatement& f = (const ForStatement&) s;
1624 return this->requirements(*f.fInitializer) |
1625 this->requirements(*f.fTest) |
1626 this->requirements(*f.fNext) |
1627 this->requirements(*f.fStatement);
1628 }
1629 case Statement::kWhile_Kind: {
1630 const WhileStatement& w = (const WhileStatement&) s;
1631 return this->requirements(*w.fTest) |
1632 this->requirements(*w.fStatement);
1633 }
1634 case Statement::kDo_Kind: {
1635 const DoStatement& d = (const DoStatement&) s;
1636 return this->requirements(*d.fTest) |
1637 this->requirements(*d.fStatement);
1638 }
1639 case Statement::kSwitch_Kind: {
1640 const SwitchStatement& sw = (const SwitchStatement&) s;
1641 Requirements result = this->requirements(*sw.fValue);
1642 for (const auto& c : sw.fCases) {
1643 for (const auto& st : c->fStatements) {
1644 result |= this->requirements(*st);
1645 }
1646 }
1647 return result;
1648 }
1649 default:
1650 return kNo_Requirements;
1651 }
1652}
1653
1654MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const FunctionDeclaration& f) {
1655 if (f.fBuiltin) {
1656 return kNo_Requirements;
1657 }
1658 auto found = fRequirements.find(&f);
1659 if (found == fRequirements.end()) {
Ethan Nicholas65a8f562019-04-19 14:00:26 -04001660 fRequirements[&f] = kNo_Requirements;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001661 for (const auto& e : fProgram) {
1662 if (ProgramElement::kFunction_Kind == e.fKind) {
1663 const FunctionDefinition& def = (const FunctionDefinition&) e;
Ethan Nicholascc305772017-10-13 16:17:45 -04001664 if (&def.fDeclaration == &f) {
1665 Requirements reqs = this->requirements(*def.fBody);
1666 fRequirements[&f] = reqs;
1667 return reqs;
1668 }
1669 }
1670 }
1671 }
1672 return found->second;
1673}
1674
Timothy Liangb8eeb802018-07-23 16:46:16 -04001675bool MetalCodeGenerator::generateCode() {
Ethan Nicholascc305772017-10-13 16:17:45 -04001676 OutputStream* rawOut = fOut;
1677 fOut = &fHeader;
Timothy Liangb8eeb802018-07-23 16:46:16 -04001678#ifdef SK_MOLTENVK
1679 fOut->write((const char*) &MVKMagicNum, sizeof(MVKMagicNum));
1680#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001681 fProgramKind = fProgram.fKind;
1682 this->writeHeader();
1683 this->writeUniformStruct();
1684 this->writeInputStruct();
Timothy Liang7d637782018-06-05 09:58:07 -04001685 this->writeOutputStruct();
1686 this->writeInterfaceBlocks();
Timothy Liangee84fe12018-05-18 14:38:19 -04001687 this->writeGlobalStruct();
Ethan Nicholascc305772017-10-13 16:17:45 -04001688 StringStream body;
1689 fOut = &body;
Ethan Nicholas3c6ae622018-04-24 13:06:09 -04001690 for (const auto& e : fProgram) {
1691 this->writeProgramElement(e);
Ethan Nicholascc305772017-10-13 16:17:45 -04001692 }
1693 fOut = rawOut;
1694
1695 write_stringstream(fHeader, *rawOut);
Chris Daltondba7aab2018-11-15 10:57:49 -05001696 write_stringstream(fExtraFunctions, *rawOut);
Ethan Nicholascc305772017-10-13 16:17:45 -04001697 write_stringstream(body, *rawOut);
Timothy Liangb8eeb802018-07-23 16:46:16 -04001698#ifdef SK_MOLTENVK
1699 this->write("\0");
1700#endif
Ethan Nicholascc305772017-10-13 16:17:45 -04001701 return true;
Ethan Nicholascc305772017-10-13 16:17:45 -04001702}
1703
1704}