Flutter Engine
The Flutter Engine
SkSLMetalCodeGenerator.cpp
Go to the documentation of this file.
1/*
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
9
10#include "include/core/SkSpan.h"
16#include "src/core/SkTHash.h"
30#include "src/sksl/SkSLString.h"
32#include "src/sksl/SkSLUtil.h"
59#include "src/sksl/ir/SkSLNop.h"
76#include "src/sksl/spirv.h"
77
78#include <algorithm>
79#include <cstddef>
80#include <cstdint>
81#include <functional>
82#include <initializer_list>
83#include <limits>
84#include <memory>
85#include <string>
86#include <string_view>
87#include <utility>
88#include <vector>
89
90using namespace skia_private;
91
92namespace SkSL {
93
95public:
97 const ShaderCaps* caps,
98 const Program* program,
100 : INHERITED(context, caps, program, out)
101 , fReservedWords({"atan2", "rsqrt", "rint", "dfdx", "dfdy", "vertex", "fragment"})
102 , fLineEnding("\n") {}
103
104 bool generateCode() override;
105
106protected:
108
110 static constexpr Requirements kNo_Requirements = 0;
111 static constexpr Requirements kInputs_Requirement = 1 << 0;
112 static constexpr Requirements kOutputs_Requirement = 1 << 1;
113 static constexpr Requirements kUniforms_Requirement = 1 << 2;
114 static constexpr Requirements kGlobals_Requirement = 1 << 3;
115 static constexpr Requirements kFragCoord_Requirement = 1 << 4;
116 static constexpr Requirements kSampleMaskIn_Requirement = 1 << 5;
117 static constexpr Requirements kVertexID_Requirement = 1 << 6;
118 static constexpr Requirements kInstanceID_Requirement = 1 << 7;
119 static constexpr Requirements kThreadgroups_Requirement = 1 << 8;
120
123
126
127 void write(std::string_view s);
128
129 void writeLine(std::string_view s = std::string_view());
130
131 void finishLine();
132
133 void writeHeader();
134
136
137 void writeUniformStruct();
138
139 void writeInputStruct();
140
141 void writeOutputStruct();
142
144
146
148
150
151 int size(const Type* type, bool isPacked) const;
152
153 int alignment(const Type* type, bool isPacked) const;
154
155 void writeGlobalStruct();
156
157 void writeGlobalInit();
158
160
162
164
165 std::string typeName(const Type& type);
166
168
169 void writeType(const Type& type);
170
171 void writeExtension(const Extension& ext);
172
173 void writeInterfaceBlock(const InterfaceBlock& intf);
174
176 const char*& separator);
177
178 void writeFunctionRequirementArgs(const FunctionDeclaration& f, const char*& separator);
179
181
183
185
186 void writeLayout(const Layout& layout);
187
189
190 void writeVarInitializer(const Variable& var, const Expression& value);
191
192 void writeName(std::string_view name);
193
194 void writeVarDeclaration(const VarDeclaration& decl);
195
196 void writeFragCoord();
197
199
200 void writeExpression(const Expression& expr, Precedence parentPrecedence);
201
202 void writeMinAbsHack(Expression& absExpr, Expression& otherExpr);
203
204 std::string getInversePolyfill(const ExpressionArray& arguments);
205
206 std::string getBitcastIntrinsic(const Type& outType);
207
208 std::string getTempVariable(const Type& varType);
209
210 void writeFunctionCall(const FunctionCall& c);
211
213 std::string getMatrixConstructHelper(const AnyConstructor& c);
214 void assembleMatrixFromMatrix(const Type& sourceMatrix, int columns, int rows);
215 void assembleMatrixFromExpressions(const AnyConstructor& ctor, int columns, int rows);
216
217 void writeMatrixCompMult();
218
219 void writeOuterProduct();
220
221 void writeMatrixTimesEqualHelper(const Type& left, const Type& right, const Type& result);
222
224
225 void writeMatrixEqualityHelpers(const Type& left, const Type& right);
226
227 std::string getVectorFromMat2x2ConstructorHelper(const Type& matrixType);
228
230
232
233 void writeEqualityHelpers(const Type& leftType, const Type& rightType);
234
235 void writeArgumentList(const ExpressionArray& arguments);
236
237 void writeSimpleIntrinsic(const FunctionCall& c);
238
239 bool writeIntrinsicCall(const FunctionCall& c, IntrinsicKind kind);
240
241 void writeConstructorCompound(const ConstructorCompound& c, Precedence parentPrecedence);
242
243 void writeConstructorCompoundVector(const ConstructorCompound& c, Precedence parentPrecedence);
244
245 void writeConstructorCompoundMatrix(const ConstructorCompound& c, Precedence parentPrecedence);
246
248 Precedence parentPrecedence);
249
251 const char* leftBracket,
252 const char* rightBracket,
253 Precedence parentPrecedence);
254
256 const char* leftBracket,
257 const char* rightBracket,
258 Precedence parentPrecedence);
259
260 void writeConstructorArrayCast(const ConstructorArrayCast& c, Precedence parentPrecedence);
261
262 void writeFieldAccess(const FieldAccess& f);
263
264 void writeSwizzle(const Swizzle& swizzle);
265
266 // Returns `floatCxR(1.0, 1.0, 1.0, 1.0, ...)`.
267 std::string splatMatrixOf1(const Type& type);
268
269 // Splats a scalar expression across a matrix of arbitrary size.
270 void writeNumberAsMatrix(const Expression& expr, const Type& matrixType);
271
273 Operator op,
274 const Expression& other,
275 Precedence precedence);
276
277 void writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence);
278
279 void writeTernaryExpression(const TernaryExpression& t, Precedence parentPrecedence);
280
281 void writeIndexExpression(const IndexExpression& expr);
282
283 void writeIndexInnerExpression(const Expression& expr);
284
285 void writePrefixExpression(const PrefixExpression& p, Precedence parentPrecedence);
286
287 void writePostfixExpression(const PostfixExpression& p, Precedence parentPrecedence);
288
289 void writeLiteral(const Literal& f);
290
291 void writeStatement(const Statement& s);
292
293 void writeStatements(const StatementArray& statements);
294
295 void writeBlock(const Block& b);
296
297 void writeIfStatement(const IfStatement& stmt);
298
299 void writeForStatement(const ForStatement& f);
300
301 void writeDoStatement(const DoStatement& d);
302
304
306
308
310
312
314
316
317 // For compute shader main functions, writes and initializes the _in and _out structs (the
318 // instances, not the types themselves)
320
321 int getUniformBinding(const Layout& layout);
322
323 int getUniformSet(const Layout& layout);
324
325 void writeWithIndexSubstitution(const std::function<void()>& fn);
326
331 const char* fLineEnding;
332 std::string fFunctionHeader;
335 int fVarCount = 0;
337 bool fAtLineStart = false;
338 // true if we have run into usages of dFdx / dFdy
339 bool fFoundDerivatives = false;
343 std::string fRTFlipName;
346 static constexpr char kTextureSuffix[] = "_Tex";
347 static constexpr char kSamplerSuffix[] = "_Smplr";
348
349 // If we might use an index expression more than once, we need to capture the result in a
350 // temporary variable to avoid double-evaluation. This should generally only occur when emitting
351 // a function call, since we need to polyfill GLSL-style out-parameter support. (skia:14130)
352 // The map holds <index-expression, temp-variable name>.
354
355 // When fIndexSubstitution is null (usually), index-substitution does not need to be performed.
361 };
362 std::unique_ptr<IndexSubstitutionData> fIndexSubstitutionData;
363
364 // Workaround/polyfill flags
365 bool fWrittenInverse2 = false, fWrittenInverse3 = false, fWrittenInverse4 = false;
368
370};
371
372static const char* operator_name(Operator op) {
373 switch (op.kind()) {
374 case Operator::Kind::LOGICALXOR: return " != ";
375 default: return op.operatorName();
376 }
377}
378
380public:
381 virtual ~GlobalStructVisitor() = default;
382 virtual void visitInterfaceBlock(const InterfaceBlock& block, std::string_view blockName) {}
383 virtual void visitTexture(const Type& type, std::string_view name) {}
384 virtual void visitSampler(const Type& type, std::string_view name) {}
385 virtual void visitConstantVariable(const VarDeclaration& decl) {}
386 virtual void visitNonconstantVariable(const Variable& var, const Expression* value) {}
387};
388
390public:
391 virtual ~ThreadgroupStructVisitor() = default;
392 virtual void visitNonconstantVariable(const Variable& var) = 0;
393};
394
395void MetalCodeGenerator::write(std::string_view s) {
396 if (s.empty()) {
397 return;
398 }
399#if defined(SK_DEBUG) || defined(SKSL_STANDALONE)
400 if (fAtLineStart) {
401 for (int i = 0; i < fIndentation; i++) {
402 fOut->writeText(" ");
403 }
404 }
405#endif
406 fOut->writeText(std::string(s).c_str());
407 fAtLineStart = false;
408}
409
410void MetalCodeGenerator::writeLine(std::string_view s) {
411 this->write(s);
413 fAtLineStart = true;
414}
415
417 if (!fAtLineStart) {
418 this->writeLine();
419 }
420}
421
423 this->writeLine("#extension " + std::string(ext.name()) + " : enable");
424}
425
426std::string MetalCodeGenerator::typeName(const Type& raw) {
427 // we need to know the modifiers for textures
428 const Type& type = raw.resolve().scalarTypeForLiteral();
429 switch (type.typeKind()) {
431 SkASSERT(!type.isUnsizedArray());
432 SkASSERTF(type.columns() > 0, "invalid array size: %s", type.description().c_str());
433 return String::printf("array<%s, %d>",
434 this->typeName(type.componentType()).c_str(), type.columns());
435
437 return this->typeName(type.componentType()) + std::to_string(type.columns());
438
440 return this->typeName(type.componentType()) + std::to_string(type.columns()) + "x" +
441 std::to_string(type.rows());
442
444 if (type.dimensions() != SpvDim2D) {
445 fContext.fErrors->error(Position(), "Unsupported texture dimensions");
446 }
447 return "sampler2D";
448
450 switch (type.textureAccess()) {
451 case Type::TextureAccess::kSample: return "texture2d<half>";
452 case Type::TextureAccess::kRead: return "texture2d<half, access::read>";
453 case Type::TextureAccess::kWrite: return "texture2d<half, access::write>";
454 case Type::TextureAccess::kReadWrite: return "texture2d<half, access::read_write>";
455 default: break;
456 }
458
460 // SkSL currently only supports the atomicUint type.
462 return "atomic_uint";
463
464 default:
465 return std::string(type.name());
466 }
467}
468
470 const Type& type = s.type();
471 this->writeLine("struct " + type.displayName() + " {");
472 fIndentation++;
473 this->writeFields(type.fields(), type.fPosition);
474 fIndentation--;
475 this->writeLine("};");
476}
477
479 this->write(this->typeName(type));
480}
481
482void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
483 switch (expr.kind()) {
484 case Expression::Kind::kBinary:
485 this->writeBinaryExpression(expr.as<BinaryExpression>(), parentPrecedence);
486 break;
487 case Expression::Kind::kConstructorArray:
488 case Expression::Kind::kConstructorStruct:
489 this->writeAnyConstructor(expr.asAnyConstructor(), "{", "}", parentPrecedence);
490 break;
491 case Expression::Kind::kConstructorArrayCast:
492 this->writeConstructorArrayCast(expr.as<ConstructorArrayCast>(), parentPrecedence);
493 break;
494 case Expression::Kind::kConstructorCompound:
495 this->writeConstructorCompound(expr.as<ConstructorCompound>(), parentPrecedence);
496 break;
497 case Expression::Kind::kConstructorDiagonalMatrix:
498 case Expression::Kind::kConstructorSplat:
499 this->writeAnyConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
500 break;
501 case Expression::Kind::kConstructorMatrixResize:
503 parentPrecedence);
504 break;
505 case Expression::Kind::kConstructorScalarCast:
506 case Expression::Kind::kConstructorCompoundCast:
507 this->writeCastConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
508 break;
510 this->write("false");
511 break;
512 case Expression::Kind::kFieldAccess:
513 this->writeFieldAccess(expr.as<FieldAccess>());
514 break;
516 this->writeLiteral(expr.as<Literal>());
517 break;
518 case Expression::Kind::kFunctionCall:
519 this->writeFunctionCall(expr.as<FunctionCall>());
520 break;
521 case Expression::Kind::kPrefix:
522 this->writePrefixExpression(expr.as<PrefixExpression>(), parentPrecedence);
523 break;
524 case Expression::Kind::kPostfix:
525 this->writePostfixExpression(expr.as<PostfixExpression>(), parentPrecedence);
526 break;
527 case Expression::Kind::kSetting:
528 this->writeExpression(*expr.as<Setting>().toLiteral(fCaps), parentPrecedence);
529 break;
530 case Expression::Kind::kSwizzle:
531 this->writeSwizzle(expr.as<Swizzle>());
532 break;
533 case Expression::Kind::kVariableReference:
535 break;
536 case Expression::Kind::kTernary:
537 this->writeTernaryExpression(expr.as<TernaryExpression>(), parentPrecedence);
538 break;
541 break;
542 default:
543 SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str());
544 break;
545 }
546}
547
548// returns true if we should pass by reference instead of by value
550 return (flags & ModifierFlag::kOut) && !type.isUnsizedArray();
551}
552
553// returns true if we need to specify an address space modifier
554static bool needs_address_space(const Type& type, ModifierFlags modifiers) {
555 return type.isUnsizedArray() || pass_by_reference(type, modifiers);
556}
557
558// returns true if the InterfaceBlock has the `buffer` modifier
559static bool is_buffer(const InterfaceBlock& block) {
560 return block.var()->modifierFlags().isBuffer();
561}
562
563// returns true if the InterfaceBlock has the `readonly` modifier
564static bool is_readonly(const InterfaceBlock& block) {
565 return block.var()->modifierFlags().isReadOnly();
566}
567
569 return "as_type<" + outType.displayName() + ">";
570}
571
573 auto oldIndexSubstitutionData = std::make_unique<IndexSubstitutionData>();
574 fIndexSubstitutionData.swap(oldIndexSubstitutionData);
575
576 // Invoke our helper function, with output going into our temporary stream.
577 {
578 AutoOutputStream outputToMainStream(this, &fIndexSubstitutionData->fMainStream);
579 fn();
580 }
581
582 if (fIndexSubstitutionData->fPrefixStream.bytesWritten() == 0) {
583 // Emit the main stream into the program as-is.
585 } else {
586 // Emit the prefix stream and main stream into the program as a sequence-expression.
587 // (Each prefix-expression must end with a comma.)
588 this->write("(");
591 this->write(")");
592 }
593
594 fIndexSubstitutionData.swap(oldIndexSubstitutionData);
595}
596
599
600 // Many intrinsics need to be rewritten in Metal.
601 if (function.isIntrinsic()) {
602 if (this->writeIntrinsicCall(c, function.intrinsicKind())) {
603 return;
604 }
605 }
606
607 // Look for out parameters. SkSL guarantees GLSL's out-param semantics, and we need to emulate
608 // it if an out-param is encountered. (Specifically, out-parameters in GLSL are only written
609 // back to the original variable at the end of the function call; also, swizzles are supported,
610 // whereas Metal doesn't allow a swizzle to be passed to a `floatN&`.)
611 const ExpressionArray& arguments = c.arguments();
612 SkSpan<Variable* const> parameters = function.parameters();
613 SkASSERT(SkToSizeT(arguments.size()) == parameters.size());
614
615 bool foundOutParam = false;
616 STArray<16, std::string> scratchVarName;
617 scratchVarName.push_back_n(arguments.size(), std::string());
618
619 for (int index = 0; index < arguments.size(); ++index) {
620 // If this is an out parameter...
621 if (parameters[index]->modifierFlags() & ModifierFlag::kOut) {
622 // Assignability was verified at IRGeneration time, so this should always succeed.
623 [[maybe_unused]] Analysis::AssignmentInfo info;
624 SkASSERT(Analysis::IsAssignable(*arguments[index], &info));
625
626 scratchVarName[index] = this->getTempVariable(arguments[index]->type());
627 foundOutParam = true;
628 }
629 }
630
631 if (foundOutParam) {
632 // Out parameters need to be written back to at the end of the function. To do this, we
633 // generate a comma-separated sequence expression that copies the out-param expressions into
634 // our temporary variables, calls the original function--storing its result into a scratch
635 // variable--and then writes the temp variables back into the original out params using the
636 // original out-param expressions. This would look something like:
637 //
638 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp), _skResult)
639 // ^ ^ ^ ^
640 // return value passes copy of argument copies back into argument return value
641 //
642 // While these expressions are complex, they allow us to maintain the proper sequencing that
643 // is necessary for out-parameters, as well as allowing us to support things like swizzles
644 // and array indices which Metal references cannot natively handle.
645
646 // We will be emitting inout expressions twice, so it's important to enable index
647 // substitution in case we encounter any side-effecting indexes.
649 this->write("((");
650
651 // ((_skResult =
652 std::string scratchResultName;
653 if (!function.returnType().isVoid()) {
654 scratchResultName = this->getTempVariable(c.type());
655 this->write(scratchResultName);
656 this->write(" = ");
657 }
658
659 // ((_skResult = func(
660 this->write(function.mangledName());
661 this->write("(");
662
663 // ((_skResult = func((_skTemp = myOutParam.x), 123
664 const char* separator = "";
665 this->writeFunctionRequirementArgs(function, separator);
666
667 for (int i = 0; i < arguments.size(); ++i) {
668 this->write(separator);
669 separator = ", ";
670 if (parameters[i]->modifierFlags() & ModifierFlag::kOut) {
671 SkASSERT(!scratchVarName[i].empty());
672 if (parameters[i]->modifierFlags() & ModifierFlag::kIn) {
673 // `inout` parameters initialize the scratch variable with the passed-in
674 // argument's value.
675 this->write("(");
676 this->write(scratchVarName[i]);
677 this->write(" = ");
678 this->writeExpression(*arguments[i], Precedence::kAssignment);
679 this->write(")");
680 } else {
681 // `out` parameters pass a reference to the uninitialized scratch variable.
682 this->write(scratchVarName[i]);
683 }
684 } else {
685 // Regular parameters are passed as-is.
686 this->writeExpression(*arguments[i], Precedence::kSequence);
687 }
688 }
689
690 // ((_skResult = func((_skTemp = myOutParam.x), 123))
691 this->write("))");
692
693 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp)
694 for (int i = 0; i < arguments.size(); ++i) {
695 if (!scratchVarName[i].empty()) {
696 this->write(", (");
697 this->writeExpression(*arguments[i], Precedence::kAssignment);
698 this->write(" = ");
699 this->write(scratchVarName[i]);
700 this->write(")");
701 }
702 }
703
704 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp),
705 // _skResult
706 if (!scratchResultName.empty()) {
707 this->write(", ");
708 this->write(scratchResultName);
709 }
710
711 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp),
712 // _skResult)
713 this->write(")");
714 });
715 } else {
716 // Emit the function call as-is, only prepending the required arguments.
717 this->write(function.mangledName());
718 this->write("(");
719 const char* separator = "";
720 this->writeFunctionRequirementArgs(function, separator);
721 for (int i = 0; i < arguments.size(); ++i) {
722 SkASSERT(scratchVarName[i].empty());
723 this->write(separator);
724 separator = ", ";
725 this->writeExpression(*arguments[i], Precedence::kSequence);
726 }
727 this->write(")");
728 }
729}
730
731static constexpr char kInverse2x2[] = R"(
732template <typename T>
733matrix<T, 2, 2> mat2_inverse(matrix<T, 2, 2> m) {
734return matrix<T, 2, 2>(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m));
735}
736)";
737
738static constexpr char kInverse3x3[] = R"(
739template <typename T>
740matrix<T, 3, 3> mat3_inverse(matrix<T, 3, 3> m) {
741T
742 a00 = m[0].x, a01 = m[0].y, a02 = m[0].z,
743 a10 = m[1].x, a11 = m[1].y, a12 = m[1].z,
744 a20 = m[2].x, a21 = m[2].y, a22 = m[2].z,
745 b01 = a22*a11 - a12*a21,
746 b11 = -a22*a10 + a12*a20,
747 b21 = a21*a10 - a11*a20,
748 det = a00*b01 + a01*b11 + a02*b21;
749return matrix<T, 3, 3>(
750 b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),
751 b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),
752 b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det);
753}
754)";
755
756static constexpr char kInverse4x4[] = R"(
757template <typename T>
758matrix<T, 4, 4> mat4_inverse(matrix<T, 4, 4> m) {
759T
760 a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w,
761 a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w,
762 a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w,
763 a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w,
764 b00 = a00*a11 - a01*a10,
765 b01 = a00*a12 - a02*a10,
766 b02 = a00*a13 - a03*a10,
767 b03 = a01*a12 - a02*a11,
768 b04 = a01*a13 - a03*a11,
769 b05 = a02*a13 - a03*a12,
770 b06 = a20*a31 - a21*a30,
771 b07 = a20*a32 - a22*a30,
772 b08 = a20*a33 - a23*a30,
773 b09 = a21*a32 - a22*a31,
774 b10 = a21*a33 - a23*a31,
775 b11 = a22*a33 - a23*a32,
776 det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;
777return matrix<T, 4, 4>(
778 a11*b11 - a12*b10 + a13*b09,
779 a02*b10 - a01*b11 - a03*b09,
780 a31*b05 - a32*b04 + a33*b03,
781 a22*b04 - a21*b05 - a23*b03,
782 a12*b08 - a10*b11 - a13*b07,
783 a00*b11 - a02*b08 + a03*b07,
784 a32*b02 - a30*b05 - a33*b01,
785 a20*b05 - a22*b02 + a23*b01,
786 a10*b10 - a11*b08 + a13*b06,
787 a01*b08 - a00*b10 - a03*b06,
788 a30*b04 - a31*b02 + a33*b00,
789 a21*b02 - a20*b04 - a23*b00,
790 a11*b07 - a10*b09 - a12*b06,
791 a00*b09 - a01*b07 + a02*b06,
792 a31*b01 - a30*b03 - a32*b00,
793 a20*b03 - a21*b01 + a22*b00) * (1/det);
794}
795)";
796
798 // Only use polyfills for a function taking a single-argument square matrix.
799 SkASSERT(arguments.size() == 1);
800 const Type& type = arguments.front()->type();
801 if (type.isMatrix() && type.rows() == type.columns()) {
802 switch (type.rows()) {
803 case 2:
804 if (!fWrittenInverse2) {
805 fWrittenInverse2 = true;
807 }
808 return "mat2_inverse";
809 case 3:
810 if (!fWrittenInverse3) {
811 fWrittenInverse3 = true;
813 }
814 return "mat3_inverse";
815 case 4:
816 if (!fWrittenInverse4) {
817 fWrittenInverse4 = true;
819 }
820 return "mat4_inverse";
821 }
822 }
823 SkDEBUGFAILF("no polyfill for inverse(%s)", type.description().c_str());
824 return "inverse";
825}
826
828 static constexpr char kMatrixCompMult[] = R"(
829template <typename T, int C, int R>
830matrix<T, C, R> matrixCompMult(matrix<T, C, R> a, const matrix<T, C, R> b) {
831 for (int c = 0; c < C; ++c) { a[c] *= b[c]; }
832 return a;
833}
834)";
837 fExtraFunctions.writeText(kMatrixCompMult);
838 }
839}
840
842 static constexpr char kOuterProduct[] = R"(
843template <typename T, int C, int R>
844matrix<T, C, R> outerProduct(const vec<T, R> a, const vec<T, C> b) {
845 matrix<T, C, R> m;
846 for (int c = 0; c < C; ++c) { m[c] = a * b[c]; }
847 return m;
848}
849)";
852 fExtraFunctions.writeText(kOuterProduct);
853 }
854}
855
857 std::string tempVar = "_skTemp" + std::to_string(fVarCount++);
858 this->fFunctionHeader += " " + this->typeName(type) + " " + tempVar + ";\n";
859 return tempVar;
860}
861
863 // Write out an intrinsic function call exactly as-is. No muss no fuss.
864 this->write(c.function().name());
865 this->writeArgumentList(c.arguments());
866}
867
869 this->write("(");
870 const char* separator = "";
871 for (const std::unique_ptr<Expression>& arg : arguments) {
872 this->write(separator);
873 separator = ", ";
874 this->writeExpression(*arg, Precedence::kSequence);
875 }
876 this->write(")");
877}
878
880 const ExpressionArray& arguments = c.arguments();
881 switch (kind) {
882 case k_textureRead_IntrinsicKind: {
883 this->writeExpression(*arguments[0], Precedence::kExpression);
884 this->write(".read(");
885 this->writeExpression(*arguments[1], Precedence::kSequence);
886 this->write(")");
887 return true;
888 }
889 case k_textureWrite_IntrinsicKind: {
890 this->writeExpression(*arguments[0], Precedence::kExpression);
891 this->write(".write(");
892 this->writeExpression(*arguments[2], Precedence::kSequence);
893 this->write(", ");
894 this->writeExpression(*arguments[1], Precedence::kSequence);
895 this->write(")");
896 return true;
897 }
898 case k_textureWidth_IntrinsicKind: {
899 this->writeExpression(*arguments[0], Precedence::kExpression);
900 this->write(".get_width()");
901 return true;
902 }
903 case k_textureHeight_IntrinsicKind: {
904 this->writeExpression(*arguments[0], Precedence::kExpression);
905 this->write(".get_height()");
906 return true;
907 }
908 case k_mod_IntrinsicKind: {
909 // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
910 std::string tmpX = this->getTempVariable(arguments[0]->type());
911 std::string tmpY = this->getTempVariable(arguments[1]->type());
912 this->write("(" + tmpX + " = ");
913 this->writeExpression(*arguments[0], Precedence::kSequence);
914 this->write(", " + tmpY + " = ");
915 this->writeExpression(*arguments[1], Precedence::kSequence);
916 this->write(", " + tmpX + " - " + tmpY + " * floor(" + tmpX + " / " + tmpY + "))");
917 return true;
918 }
919 // GLSL declares scalar versions of most geometric intrinsics, but these don't exist in MSL
920 case k_distance_IntrinsicKind: {
921 if (arguments[0]->type().columns() == 1) {
922 this->write("abs(");
923 this->writeExpression(*arguments[0], Precedence::kAdditive);
924 this->write(" - ");
925 this->writeExpression(*arguments[1], Precedence::kAdditive);
926 this->write(")");
927 } else {
928 this->writeSimpleIntrinsic(c);
929 }
930 return true;
931 }
932 case k_dot_IntrinsicKind: {
933 if (arguments[0]->type().columns() == 1) {
934 this->write("(");
935 this->writeExpression(*arguments[0], Precedence::kMultiplicative);
936 this->write(" * ");
937 this->writeExpression(*arguments[1], Precedence::kMultiplicative);
938 this->write(")");
939 } else {
940 this->writeSimpleIntrinsic(c);
941 }
942 return true;
943 }
944 case k_faceforward_IntrinsicKind: {
945 if (arguments[0]->type().columns() == 1) {
946 // ((((Nref) * (I) < 0) ? 1 : -1) * (N))
947 this->write("((((");
948 this->writeExpression(*arguments[2], Precedence::kSequence);
949 this->write(") * (");
950 this->writeExpression(*arguments[1], Precedence::kSequence);
951 this->write(") < 0) ? 1 : -1) * (");
952 this->writeExpression(*arguments[0], Precedence::kSequence);
953 this->write("))");
954 } else {
955 this->writeSimpleIntrinsic(c);
956 }
957 return true;
958 }
959 case k_length_IntrinsicKind: {
960 this->write(arguments[0]->type().columns() == 1 ? "abs(" : "length(");
961 this->writeExpression(*arguments[0], Precedence::kSequence);
962 this->write(")");
963 return true;
964 }
965 case k_normalize_IntrinsicKind: {
966 this->write(arguments[0]->type().columns() == 1 ? "sign(" : "normalize(");
967 this->writeExpression(*arguments[0], Precedence::kSequence);
968 this->write(")");
969 return true;
970 }
971 case k_packUnorm2x16_IntrinsicKind: {
972 this->write("pack_float_to_unorm2x16(");
973 this->writeExpression(*arguments[0], Precedence::kSequence);
974 this->write(")");
975 return true;
976 }
977 case k_unpackUnorm2x16_IntrinsicKind: {
978 this->write("unpack_unorm2x16_to_float(");
979 this->writeExpression(*arguments[0], Precedence::kSequence);
980 this->write(")");
981 return true;
982 }
983 case k_packSnorm2x16_IntrinsicKind: {
984 this->write("pack_float_to_snorm2x16(");
985 this->writeExpression(*arguments[0], Precedence::kSequence);
986 this->write(")");
987 return true;
988 }
989 case k_unpackSnorm2x16_IntrinsicKind: {
990 this->write("unpack_snorm2x16_to_float(");
991 this->writeExpression(*arguments[0], Precedence::kSequence);
992 this->write(")");
993 return true;
994 }
995 case k_packUnorm4x8_IntrinsicKind: {
996 this->write("pack_float_to_unorm4x8(");
997 this->writeExpression(*arguments[0], Precedence::kSequence);
998 this->write(")");
999 return true;
1000 }
1001 case k_unpackUnorm4x8_IntrinsicKind: {
1002 this->write("unpack_unorm4x8_to_float(");
1003 this->writeExpression(*arguments[0], Precedence::kSequence);
1004 this->write(")");
1005 return true;
1006 }
1007 case k_packSnorm4x8_IntrinsicKind: {
1008 this->write("pack_float_to_snorm4x8(");
1009 this->writeExpression(*arguments[0], Precedence::kSequence);
1010 this->write(")");
1011 return true;
1012 }
1013 case k_unpackSnorm4x8_IntrinsicKind: {
1014 this->write("unpack_snorm4x8_to_float(");
1015 this->writeExpression(*arguments[0], Precedence::kSequence);
1016 this->write(")");
1017 return true;
1018 }
1019 case k_packHalf2x16_IntrinsicKind: {
1020 this->write("as_type<uint>(half2(");
1021 this->writeExpression(*arguments[0], Precedence::kSequence);
1022 this->write("))");
1023 return true;
1024 }
1025 case k_unpackHalf2x16_IntrinsicKind: {
1026 this->write("float2(as_type<half2>(");
1027 this->writeExpression(*arguments[0], Precedence::kSequence);
1028 this->write("))");
1029 return true;
1030 }
1031 case k_floatBitsToInt_IntrinsicKind:
1032 case k_floatBitsToUint_IntrinsicKind:
1033 case k_intBitsToFloat_IntrinsicKind:
1034 case k_uintBitsToFloat_IntrinsicKind: {
1035 this->write(this->getBitcastIntrinsic(c.type()));
1036 this->write("(");
1037 this->writeExpression(*arguments[0], Precedence::kSequence);
1038 this->write(")");
1039 return true;
1040 }
1041 case k_degrees_IntrinsicKind: {
1042 this->write("((");
1043 this->writeExpression(*arguments[0], Precedence::kSequence);
1044 this->write(") * 57.2957795)");
1045 return true;
1046 }
1047 case k_radians_IntrinsicKind: {
1048 this->write("((");
1049 this->writeExpression(*arguments[0], Precedence::kSequence);
1050 this->write(") * 0.0174532925)");
1051 return true;
1052 }
1053 case k_dFdx_IntrinsicKind: {
1054 this->write("dfdx");
1055 this->writeArgumentList(c.arguments());
1056 return true;
1057 }
1058 case k_dFdy_IntrinsicKind: {
1059 if (!fRTFlipName.empty()) {
1060 this->write("(" + fRTFlipName + ".y * dfdy");
1061 } else {
1062 this->write("(dfdy");
1063 }
1064 this->writeArgumentList(c.arguments());
1065 this->write(")");
1066 return true;
1067 }
1068 case k_inverse_IntrinsicKind: {
1069 this->write(this->getInversePolyfill(arguments));
1070 this->writeArgumentList(c.arguments());
1071 return true;
1072 }
1073 case k_inversesqrt_IntrinsicKind: {
1074 this->write("rsqrt");
1075 this->writeArgumentList(c.arguments());
1076 return true;
1077 }
1078 case k_atan_IntrinsicKind: {
1079 this->write(c.arguments().size() == 2 ? "atan2" : "atan");
1080 this->writeArgumentList(c.arguments());
1081 return true;
1082 }
1083 case k_reflect_IntrinsicKind: {
1084 if (arguments[0]->type().columns() == 1) {
1085 // We need to synthesize `I - 2 * N * I * N`.
1086 std::string tmpI = this->getTempVariable(arguments[0]->type());
1087 std::string tmpN = this->getTempVariable(arguments[1]->type());
1088
1089 // (_skTempI = ...
1090 this->write("(" + tmpI + " = ");
1091 this->writeExpression(*arguments[0], Precedence::kSequence);
1092
1093 // , _skTempN = ...
1094 this->write(", " + tmpN + " = ");
1095 this->writeExpression(*arguments[1], Precedence::kSequence);
1096
1097 // , _skTempI - 2 * _skTempN * _skTempI * _skTempN)
1098 this->write(", " + tmpI + " - 2 * " + tmpN + " * " + tmpI + " * " + tmpN + ")");
1099 } else {
1100 this->writeSimpleIntrinsic(c);
1101 }
1102 return true;
1103 }
1104 case k_refract_IntrinsicKind: {
1105 if (arguments[0]->type().columns() == 1) {
1106 // Metal does implement refract for vectors; rather than reimplementing refract from
1107 // scratch, we can replace the call with `refract(float2(I,0), float2(N,0), eta).x`.
1108 this->write("(refract(float2(");
1109 this->writeExpression(*arguments[0], Precedence::kSequence);
1110 this->write(", 0), float2(");
1111 this->writeExpression(*arguments[1], Precedence::kSequence);
1112 this->write(", 0), ");
1113 this->writeExpression(*arguments[2], Precedence::kSequence);
1114 this->write(").x)");
1115 } else {
1116 this->writeSimpleIntrinsic(c);
1117 }
1118 return true;
1119 }
1120 case k_roundEven_IntrinsicKind: {
1121 this->write("rint");
1122 this->writeArgumentList(c.arguments());
1123 return true;
1124 }
1125 case k_bitCount_IntrinsicKind: {
1126 this->write("popcount(");
1127 this->writeExpression(*arguments[0], Precedence::kSequence);
1128 this->write(")");
1129 return true;
1130 }
1131 case k_findLSB_IntrinsicKind: {
1132 // Create a temp variable to store the expression, to avoid double-evaluating it.
1133 std::string skTemp = this->getTempVariable(arguments[0]->type());
1134 std::string exprType = this->typeName(arguments[0]->type());
1135
1136 // ctz returns numbits(type) on zero inputs; GLSL documents it as generating -1 instead.
1137 // Use select to detect zero inputs and force a -1 result.
1138
1139 // (_skTemp1 = (.....), select(ctz(_skTemp1), int4(-1), _skTemp1 == int4(0)))
1140 this->write("(");
1141 this->write(skTemp);
1142 this->write(" = (");
1143 this->writeExpression(*arguments[0], Precedence::kSequence);
1144 this->write("), select(ctz(");
1145 this->write(skTemp);
1146 this->write("), ");
1147 this->write(exprType);
1148 this->write("(-1), ");
1149 this->write(skTemp);
1150 this->write(" == ");
1151 this->write(exprType);
1152 this->write("(0)))");
1153 return true;
1154 }
1155 case k_findMSB_IntrinsicKind: {
1156 // Create a temp variable to store the expression, to avoid double-evaluating it.
1157 std::string skTemp1 = this->getTempVariable(arguments[0]->type());
1158 std::string exprType = this->typeName(arguments[0]->type());
1159
1160 // GLSL findMSB is actually quite different from Metal's clz:
1161 // - For signed negative numbers, it returns the first zero bit, not the first one bit!
1162 // - For an empty input (0/~0 depending on sign), findMSB gives -1; clz is numbits(type)
1163
1164 // (_skTemp1 = (.....),
1165 this->write("(");
1166 this->write(skTemp1);
1167 this->write(" = (");
1168 this->writeExpression(*arguments[0], Precedence::kSequence);
1169 this->write("), ");
1170
1171 // Signed input types might be negative; we need another helper variable to negate the
1172 // input (since we can only find one bits, not zero bits).
1173 std::string skTemp2;
1174 if (arguments[0]->type().isSigned()) {
1175 // ... _skTemp2 = (select(_skTemp1, ~_skTemp1, _skTemp1 < 0)),
1176 skTemp2 = this->getTempVariable(arguments[0]->type());
1177 this->write(skTemp2);
1178 this->write(" = (select(");
1179 this->write(skTemp1);
1180 this->write(", ~");
1181 this->write(skTemp1);
1182 this->write(", ");
1183 this->write(skTemp1);
1184 this->write(" < 0)), ");
1185 } else {
1186 skTemp2 = skTemp1;
1187 }
1188
1189 // ... select(int4(clz(_skTemp2)), int4(-1), _skTemp2 == int4(0)))
1190 this->write("select(");
1191 this->write(this->typeName(c.type()));
1192 this->write("(clz(");
1193 this->write(skTemp2);
1194 this->write(")), ");
1195 this->write(this->typeName(c.type()));
1196 this->write("(-1), ");
1197 this->write(skTemp2);
1198 this->write(" == ");
1199 this->write(exprType);
1200 this->write("(0)))");
1201 return true;
1202 }
1203 case k_sign_IntrinsicKind: {
1204 if (arguments[0]->type().componentType().isInteger()) {
1205 // Create a temp variable to store the expression, to avoid double-evaluating it.
1206 std::string skTemp = this->getTempVariable(arguments[0]->type());
1207 std::string exprType = this->typeName(arguments[0]->type());
1208
1209 // (_skTemp = (.....),
1210 this->write("(");
1211 this->write(skTemp);
1212 this->write(" = (");
1213 this->writeExpression(*arguments[0], Precedence::kSequence);
1214 this->write("), ");
1215
1216 // ... select(select(int4(0), int4(-1), _skTemp < 0), int4(1), _skTemp > 0))
1217 this->write("select(select(");
1218 this->write(exprType);
1219 this->write("(0), ");
1220 this->write(exprType);
1221 this->write("(-1), ");
1222 this->write(skTemp);
1223 this->write(" < 0), ");
1224 this->write(exprType);
1225 this->write("(1), ");
1226 this->write(skTemp);
1227 this->write(" > 0))");
1228 } else {
1229 this->writeSimpleIntrinsic(c);
1230 }
1231 return true;
1232 }
1233 case k_matrixCompMult_IntrinsicKind: {
1234 this->writeMatrixCompMult();
1235 this->writeSimpleIntrinsic(c);
1236 return true;
1237 }
1238 case k_outerProduct_IntrinsicKind: {
1239 this->writeOuterProduct();
1240 this->writeSimpleIntrinsic(c);
1241 return true;
1242 }
1243 case k_mix_IntrinsicKind: {
1244 SkASSERT(c.arguments().size() == 3);
1245 if (arguments[2]->type().componentType().isBoolean()) {
1246 // The Boolean forms of GLSL mix() use the select() intrinsic in Metal.
1247 this->write("select");
1248 this->writeArgumentList(c.arguments());
1249 return true;
1250 }
1251 // The basic form of mix() is supported by Metal as-is.
1252 this->writeSimpleIntrinsic(c);
1253 return true;
1254 }
1255 case k_equal_IntrinsicKind:
1256 case k_greaterThan_IntrinsicKind:
1257 case k_greaterThanEqual_IntrinsicKind:
1258 case k_lessThan_IntrinsicKind:
1259 case k_lessThanEqual_IntrinsicKind:
1260 case k_notEqual_IntrinsicKind: {
1261 this->write("(");
1262 this->writeExpression(*c.arguments()[0], Precedence::kRelational);
1263 switch (kind) {
1264 case k_equal_IntrinsicKind:
1265 this->write(" == ");
1266 break;
1267 case k_notEqual_IntrinsicKind:
1268 this->write(" != ");
1269 break;
1270 case k_lessThan_IntrinsicKind:
1271 this->write(" < ");
1272 break;
1273 case k_lessThanEqual_IntrinsicKind:
1274 this->write(" <= ");
1275 break;
1276 case k_greaterThan_IntrinsicKind:
1277 this->write(" > ");
1278 break;
1279 case k_greaterThanEqual_IntrinsicKind:
1280 this->write(" >= ");
1281 break;
1282 default:
1283 SK_ABORT("unsupported comparison intrinsic kind");
1284 }
1285 this->writeExpression(*c.arguments()[1], Precedence::kRelational);
1286 this->write(")");
1287 return true;
1288 }
1289 case k_storageBarrier_IntrinsicKind:
1290 this->write("threadgroup_barrier(mem_flags::mem_device)");
1291 return true;
1292 case k_workgroupBarrier_IntrinsicKind:
1293 this->write("threadgroup_barrier(mem_flags::mem_threadgroup)");
1294 return true;
1295 case k_atomicAdd_IntrinsicKind:
1296 this->write("atomic_fetch_add_explicit(&");
1297 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1298 this->write(", ");
1299 this->writeExpression(*c.arguments()[1], Precedence::kSequence);
1300 this->write(", memory_order_relaxed)");
1301 return true;
1302 case k_atomicLoad_IntrinsicKind:
1303 this->write("atomic_load_explicit(&");
1304 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1305 this->write(", memory_order_relaxed)");
1306 return true;
1307 case k_atomicStore_IntrinsicKind:
1308 this->write("atomic_store_explicit(&");
1309 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1310 this->write(", ");
1311 this->writeExpression(*c.arguments()[1], Precedence::kSequence);
1312 this->write(", memory_order_relaxed)");
1313 return true;
1314 case k_loadFloatBuffer_IntrinsicKind: {
1315 auto indexExpression = IRHelpers::LoadFloatBuffer(
1316 fContext,
1317 fCaps,
1318 c.position(),
1319 c.arguments()[0]->clone());
1320
1321 this->writeExpression(*indexExpression, Precedence::kExpression);
1322 return true;
1323 }
1324 default:
1325 return false;
1326 }
1327}
1328
1329// Assembles a matrix of type floatRxC by resizing another matrix named `x0`.
1330// Cells that don't exist in the source matrix will be populated with identity-matrix values.
1331void MetalCodeGenerator::assembleMatrixFromMatrix(const Type& sourceMatrix, int columns, int rows) {
1332 SkASSERT(rows <= 4);
1333 SkASSERT(columns <= 4);
1334
1335 std::string matrixType = this->typeName(sourceMatrix.componentType());
1336
1337 const char* separator = "";
1338 for (int c = 0; c < columns; ++c) {
1339 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1340 separator = "), ";
1341
1342 // Determine how many values to take from the source matrix for this row.
1343 int swizzleLength = 0;
1344 if (c < sourceMatrix.columns()) {
1345 swizzleLength = std::min<>(rows, sourceMatrix.rows());
1346 }
1347
1348 // Emit all the values from the source matrix row.
1349 bool firstItem;
1350 switch (swizzleLength) {
1351 case 0: firstItem = true; break;
1352 case 1: firstItem = false; fExtraFunctions.printf("x0[%d].x", c); break;
1353 case 2: firstItem = false; fExtraFunctions.printf("x0[%d].xy", c); break;
1354 case 3: firstItem = false; fExtraFunctions.printf("x0[%d].xyz", c); break;
1355 case 4: firstItem = false; fExtraFunctions.printf("x0[%d].xyzw", c); break;
1356 default: SkUNREACHABLE;
1357 }
1358
1359 // Emit the placeholder identity-matrix cells.
1360 for (int r = swizzleLength; r < rows; ++r) {
1361 fExtraFunctions.printf("%s%s", firstItem ? "" : ", ", (r == c) ? "1.0" : "0.0");
1362 firstItem = false;
1363 }
1364 }
1365
1367}
1368
1369// Assembles a matrix of type floatCxR by concatenating an arbitrary mix of values, named `x0`,
1370// `x1`, etc. An error is written if the expression list don't contain exactly C*R scalars.
1372 int columns,
1373 int rows) {
1374 SkASSERT(rows <= 4);
1375 SkASSERT(columns <= 4);
1376
1377 std::string matrixType = this->typeName(ctor.type().componentType());
1378 size_t argIndex = 0;
1379 int argPosition = 0;
1380 auto args = ctor.argumentSpan();
1381
1382 static constexpr char kSwizzle[] = "xyzw";
1383 const char* separator = "";
1384 for (int c = 0; c < columns; ++c) {
1385 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1386 separator = "), ";
1387
1388 const char* columnSeparator = "";
1389 for (int r = 0; r < rows;) {
1390 fExtraFunctions.writeText(columnSeparator);
1391 columnSeparator = ", ";
1392
1393 if (argIndex < args.size()) {
1394 const Type& argType = args[argIndex]->type();
1395 switch (argType.typeKind()) {
1397 fExtraFunctions.printf("x%zu", argIndex);
1398 ++r;
1399 ++argPosition;
1400 break;
1401 }
1403 fExtraFunctions.printf("x%zu.", argIndex);
1404 do {
1405 fExtraFunctions.write8(kSwizzle[argPosition]);
1406 ++r;
1407 ++argPosition;
1408 } while (r < rows && argPosition < argType.columns());
1409 break;
1410 }
1412 fExtraFunctions.printf("x%zu[%d].", argIndex, argPosition / argType.rows());
1413 do {
1414 fExtraFunctions.write8(kSwizzle[argPosition]);
1415 ++r;
1416 ++argPosition;
1417 } while (r < rows && (argPosition % argType.rows()) != 0);
1418 break;
1419 }
1420 default: {
1421 SkDEBUGFAIL("incorrect type of argument for matrix constructor");
1422 fExtraFunctions.writeText("<error>");
1423 break;
1424 }
1425 }
1426
1427 if (argPosition >= argType.columns() * argType.rows()) {
1428 ++argIndex;
1429 argPosition = 0;
1430 }
1431 } else {
1432 SkDEBUGFAIL("not enough arguments for matrix constructor");
1433 fExtraFunctions.writeText("<error>");
1434 }
1435 }
1436 }
1437
1438 if (argPosition != 0 || argIndex != args.size()) {
1439 SkDEBUGFAIL("incorrect number of arguments for matrix constructor");
1440 fExtraFunctions.writeText(", <error>");
1441 }
1442
1444}
1445
1446// Generates a constructor for 'matrix' which reorganizes the input arguments into the proper shape.
1447// Keeps track of previously generated constructors so that we won't generate more than one
1448// constructor for any given permutation of input argument types. Returns the name of the
1449// generated constructor method.
1451 const Type& type = c.type();
1452 int columns = type.columns();
1453 int rows = type.rows();
1454 auto args = c.argumentSpan();
1455 std::string typeName = this->typeName(type);
1456
1457 // Create the helper-method name and use it as our lookup key.
1458 std::string name = String::printf("%s_from", typeName.c_str());
1459 for (const std::unique_ptr<Expression>& expr : args) {
1460 String::appendf(&name, "_%s", this->typeName(expr->type()).c_str());
1461 }
1462
1463 // If a helper-method has not been synthesized yet, create it now.
1464 if (!fHelpers.contains(name)) {
1465 fHelpers.add(name);
1466
1467 // Unlike GLSL, Metal requires that matrices are initialized with exactly R vectors of C
1468 // components apiece. (In Metal 2.0, you can also supply R*C scalars, but you still cannot
1469 // supply a mixture of scalars and vectors.)
1470 fExtraFunctions.printf("%s %s(", typeName.c_str(), name.c_str());
1471
1472 size_t argIndex = 0;
1473 const char* argSeparator = "";
1474 for (const std::unique_ptr<Expression>& expr : args) {
1475 fExtraFunctions.printf("%s%s x%zu", argSeparator,
1476 this->typeName(expr->type()).c_str(), argIndex++);
1477 argSeparator = ", ";
1478 }
1479
1480 fExtraFunctions.printf(") {\n return %s(", typeName.c_str());
1481
1482 if (args.size() == 1 && args.front()->type().isMatrix()) {
1483 this->assembleMatrixFromMatrix(args.front()->type(), columns, rows);
1484 } else {
1485 this->assembleMatrixFromExpressions(c, columns, rows);
1486 }
1487
1488 fExtraFunctions.writeText(");\n}\n");
1489 }
1490 return name;
1491}
1492
1494 SkASSERT(c.type().isMatrix());
1495
1496 // GLSL is fairly free-form about inputs to its matrix constructors, but Metal is not; it
1497 // expects exactly R vectors of C components apiece. (Metal 2.0 also allows a list of R*C
1498 // scalars.) Some cases are simple to translate and so we handle those inline--e.g. a list of
1499 // scalars can be constructed trivially. In more complex cases, we generate a helper function
1500 // that converts our inputs into a properly-shaped matrix.
1501 // A matrix construct helper method is always used if any input argument is a matrix.
1502 // Helper methods are also necessary when any argument would span multiple rows. For instance:
1503 //
1504 // float2 x = (1, 2);
1505 // float3x2(x, 3, 4, 5, 6) = | 1 3 5 | = no helper needed; conversion can be done inline
1506 // | 2 4 6 |
1507 //
1508 // float2 x = (2, 3);
1509 // float3x2(1, x, 4, 5, 6) = | 1 3 5 | = x spans multiple rows; a helper method will be used
1510 // | 2 4 6 |
1511 //
1512 // float4 x = (1, 2, 3, 4);
1513 // float2x2(x) = | 1 3 | = x spans multiple rows; a helper method will be used
1514 // | 2 4 |
1515 //
1516
1517 int position = 0;
1518 for (const std::unique_ptr<Expression>& expr : c.arguments()) {
1519 // If an input argument is a matrix, we need a helper function.
1520 if (expr->type().isMatrix()) {
1521 return true;
1522 }
1523 position += expr->type().columns();
1524 if (position > c.type().rows()) {
1525 // An input argument would span multiple rows; a helper function is required.
1526 return true;
1527 }
1528 if (position == c.type().rows()) {
1529 // We've advanced to the end of a row. Wrap to the start of the next row.
1530 position = 0;
1531 }
1532 }
1533
1534 return false;
1535}
1536
1538 Precedence parentPrecedence) {
1539 // Matrix-resize via casting doesn't natively exist in Metal at all, so we always need to use a
1540 // matrix-construct helper here.
1541 this->write(this->getMatrixConstructHelper(c));
1542 this->write("(");
1543 this->writeExpression(*c.argument(), Precedence::kSequence);
1544 this->write(")");
1545}
1546
1548 Precedence parentPrecedence) {
1549 if (c.type().isVector()) {
1550 this->writeConstructorCompoundVector(c, parentPrecedence);
1551 } else if (c.type().isMatrix()) {
1552 this->writeConstructorCompoundMatrix(c, parentPrecedence);
1553 } else {
1554 fContext.fErrors->error(c.fPosition, "unsupported compound constructor");
1555 }
1556}
1557
1559 Precedence parentPrecedence) {
1560 const Type& inType = c.argument()->type().componentType();
1561 const Type& outType = c.type().componentType();
1562 std::string inTypeName = this->typeName(inType);
1563 std::string outTypeName = this->typeName(outType);
1564
1565 std::string name = "array_of_" + outTypeName + "_from_" + inTypeName;
1566 if (!fHelpers.contains(name)) {
1567 fHelpers.add(name);
1569template <size_t N>
1570array<%s, N> %s(thread const array<%s, N>& x) {
1571 array<%s, N> result;
1572 for (int i = 0; i < N; ++i) {
1573 result[i] = %s(x[i]);
1574 }
1575 return result;
1576}
1577)",
1578 outTypeName.c_str(), name.c_str(), inTypeName.c_str(),
1579 outTypeName.c_str(),
1580 outTypeName.c_str());
1581 }
1582
1583 this->write(name);
1584 this->write("(");
1585 this->writeExpression(*c.argument(), Precedence::kSequence);
1586 this->write(")");
1587}
1588
1590 SkASSERT(matrixType.isMatrix());
1591 SkASSERT(matrixType.rows() == 2);
1592 SkASSERT(matrixType.columns() == 2);
1593
1594 std::string baseType = this->typeName(matrixType.componentType());
1595 std::string name = String::printf("%s4_from_%s2x2", baseType.c_str(), baseType.c_str());
1596 if (!fHelpers.contains(name)) {
1597 fHelpers.add(name);
1598
1600%s4 %s(%s2x2 x) {
1601 return %s4(x[0].xy, x[1].xy);
1602}
1603)", baseType.c_str(), name.c_str(), baseType.c_str(), baseType.c_str());
1604 }
1605
1606 return name;
1607}
1608
1610 Precedence parentPrecedence) {
1611 SkASSERT(c.type().isVector());
1612
1613 // Metal supports constructing vectors from a mix of scalars and vectors, but not matrices.
1614 // GLSL supports vec4(mat2x2), so we detect that case here and emit a helper function.
1615 if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
1616 const Expression& expr = *c.argumentSpan().front();
1617 if (expr.type().isMatrix()) {
1618 this->write(this->getVectorFromMat2x2ConstructorHelper(expr.type()));
1619 this->write("(");
1620 this->writeExpression(expr, Precedence::kSequence);
1621 this->write(")");
1622 return;
1623 }
1624 }
1625
1626 this->writeAnyConstructor(c, "(", ")", parentPrecedence);
1627}
1628
1630 Precedence parentPrecedence) {
1631 SkASSERT(c.type().isMatrix());
1632
1633 // Emit and invoke a matrix-constructor helper method if one is necessary.
1634 if (this->matrixConstructHelperIsNeeded(c)) {
1635 this->write(this->getMatrixConstructHelper(c));
1636 this->write("(");
1637 const char* separator = "";
1638 for (const std::unique_ptr<Expression>& expr : c.arguments()) {
1639 this->write(separator);
1640 separator = ", ";
1641 this->writeExpression(*expr, Precedence::kSequence);
1642 }
1643 this->write(")");
1644 return;
1645 }
1646
1647 // Metal doesn't allow creating matrices by passing in scalars and vectors in a jumble; it
1648 // requires your scalars to be grouped up into columns. Because `matrixConstructHelperIsNeeded`
1649 // returned false, we know that none of our scalars/vectors "wrap" across across a column, so we
1650 // can group our inputs up and synthesize a constructor for each column.
1651 const Type& matrixType = c.type();
1652 const Type& columnType = matrixType.columnType(fContext);
1653
1654 this->writeType(matrixType);
1655 this->write("(");
1656 const char* separator = "";
1657 int scalarCount = 0;
1658 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
1659 this->write(separator);
1660 separator = ", ";
1661 if (arg->type().columns() < matrixType.rows()) {
1662 // Write a `floatN(` constructor to group scalars and smaller vectors together.
1663 if (!scalarCount) {
1664 this->writeType(columnType);
1665 this->write("(");
1666 }
1667 scalarCount += arg->type().columns();
1668 }
1669 this->writeExpression(*arg, Precedence::kSequence);
1670 if (scalarCount && scalarCount == matrixType.rows()) {
1671 // Close our `floatN(...` constructor block from above.
1672 this->write(")");
1673 scalarCount = 0;
1674 }
1675 }
1676 this->write(")");
1677}
1678
1680 const char* leftBracket,
1681 const char* rightBracket,
1682 Precedence parentPrecedence) {
1683 this->writeType(c.type());
1684 this->write(leftBracket);
1685 const char* separator = "";
1686 for (const std::unique_ptr<Expression>& arg : c.argumentSpan()) {
1687 this->write(separator);
1688 separator = ", ";
1689 this->writeExpression(*arg, Precedence::kSequence);
1690 }
1691 this->write(rightBracket);
1692}
1693
1695 const char* leftBracket,
1696 const char* rightBracket,
1697 Precedence parentPrecedence) {
1698 return this->writeAnyConstructor(c, leftBracket, rightBracket, parentPrecedence);
1699}
1700
1702 if (!fRTFlipName.empty()) {
1703 this->write("float4(_fragCoord.x, ");
1704 this->write(fRTFlipName.c_str());
1705 this->write(".x + ");
1706 this->write(fRTFlipName.c_str());
1707 this->write(".y * _fragCoord.y, 0.0, _fragCoord.w)");
1708 } else {
1709 this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
1710 }
1711}
1712
1713static bool is_compute_builtin(const Variable& var) {
1714 switch (var.layout().fBuiltin) {
1720 return true;
1721 default:
1722 break;
1723 }
1724 return false;
1725}
1726
1727// true if the var is part of the Inputs struct
1728static bool is_input(const Variable& var) {
1730 return var.modifierFlags() & ModifierFlag::kIn &&
1731 (var.layout().fBuiltin == -1 || is_compute_builtin(var)) &&
1733}
1734
1735// true if the var is part of the Outputs struct
1736static bool is_output(const Variable& var) {
1738 // inout vars get written into the Inputs struct, so we exclude them from Outputs
1739 return (var.modifierFlags() & ModifierFlag::kOut) &&
1740 !(var.modifierFlags() & ModifierFlag::kIn) &&
1741 var.layout().fBuiltin == -1 &&
1743}
1744
1745// true if the var is part of the Uniforms struct
1746static bool is_uniforms(const Variable& var) {
1748 return var.modifierFlags().isUniform() &&
1750}
1751
1752// true if the var is part of the Threadgroups struct
1753static bool is_threadgroup(const Variable& var) {
1755 return var.modifierFlags().isWorkgroup();
1756}
1757
1758// true if the var is part of the Globals struct
1759static bool is_in_globals(const Variable& var) {
1761 return !var.modifierFlags().isConst();
1762}
1763
1765 switch (ref.variable()->layout().fBuiltin) {
1767 this->write("_out.sk_FragColor");
1768 break;
1770 this->write("_out.sk_SampleMask");
1771 break;
1774 this->write("_out.sk_SecondaryFragColor");
1775 } else {
1776 fContext.fErrors->error(ref.position(), "'sk_SecondaryFragColor' not supported");
1777 }
1778 break;
1780 this->writeFragCoord();
1781 break;
1783 this->write("sk_SampleMaskIn");
1784 break;
1786 this->write("sk_VertexID");
1787 break;
1789 this->write("sk_InstanceID");
1790 break;
1792 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
1793 // clockwise to match Skia convention.
1794 if (!fRTFlipName.empty()) {
1795 this->write("(" + fRTFlipName + ".y < 0 ? _frontFacing : !_frontFacing)");
1796 } else {
1797 this->write("_frontFacing");
1798 }
1799 break;
1803 } else {
1804 fContext.fErrors->error(ref.position(), "'sk_LastFragColor' not supported");
1805 }
1806 break;
1807 default:
1808 const Variable& var = *ref.variable();
1809 if (var.storage() == Variable::Storage::kGlobal) {
1810 if (is_input(var)) {
1811 this->write("_in.");
1812 } else if (is_output(var)) {
1813 this->write("_out.");
1814 } else if (is_uniforms(var)) {
1815 this->write("_uniforms.");
1816 } else if (is_threadgroup(var)) {
1817 this->write("_threadgroups.");
1818 } else if (is_in_globals(var)) {
1819 this->write("_globals.");
1820 }
1821 }
1822 this->writeName(var.mangledName());
1823 }
1824}
1825
1828 // If this expression already exists in the index-substitution map, use the substitute.
1829 if (const std::string* existing = fIndexSubstitutionData->fMap.find(&expr)) {
1830 this->write(*existing);
1831 return;
1832 }
1833
1834 // If this expression is non-trivial, we will need to create a scratch variable and store
1835 // its value there.
1836 if (fIndexSubstitutionData->fCreateSubstitutes && !Analysis::IsTrivialExpression(expr)) {
1837 // Create a substitute variable and emit it into the main stream.
1838 std::string scratchVar = this->getTempVariable(expr.type());
1839 this->write(scratchVar);
1840
1841 // Initialize the substitute variable in the prefix-stream.
1842 AutoOutputStream outputToPrefixStream(this, &fIndexSubstitutionData->fPrefixStream);
1843 this->write(scratchVar);
1844 this->write(" = ");
1845 this->writeExpression(expr, Precedence::kAssignment);
1846 this->write(", ");
1847
1848 // Remember the substitute variable in our map.
1849 fIndexSubstitutionData->fMap.set(&expr, std::move(scratchVar));
1850 return;
1851 }
1852 }
1853
1854 // We don't require index-substitution; just emit the expression normally.
1855 this->writeExpression(expr, Precedence::kExpression);
1856}
1857
1859 // Metal does not seem to handle assignment into `vec.zyx[i]` properly--it compiles, but the
1860 // results are wrong. We rewrite the expression as `vec[uint3(2,1,0)[i]]` instead. (Filed with
1861 // Apple as FB12055941.)
1862 if (expr.base()->is<Swizzle>() && expr.base()->as<Swizzle>().components().size() > 1) {
1863 const Swizzle& swizzle = expr.base()->as<Swizzle>();
1864 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1865 this->write("[uint" + std::to_string(swizzle.components().size()) + "(");
1866 auto separator = SkSL::String::Separator();
1867 for (int8_t component : swizzle.components()) {
1868 this->write(separator());
1869 this->write(std::to_string(component));
1870 }
1871 this->write(")[");
1872 this->writeIndexInnerExpression(*expr.index());
1873 this->write("]]");
1874 } else {
1875 this->writeExpression(*expr.base(), Precedence::kPostfix);
1876 this->write("[");
1877 this->writeIndexInnerExpression(*expr.index());
1878 this->write("]");
1879 }
1880}
1881
1883 const Field* field = &f.base()->type().fields()[f.fieldIndex()];
1884 if (FieldAccess::OwnerKind::kDefault == f.ownerKind()) {
1885 this->writeExpression(*f.base(), Precedence::kPostfix);
1886 this->write(".");
1887 }
1888 switch (field->fLayout.fBuiltin) {
1890 this->write("_out.sk_Position");
1891 break;
1893 this->write("_out.sk_PointSize");
1894 break;
1895 default:
1896 if (FieldAccess::OwnerKind::kAnonymousInterfaceBlock == f.ownerKind()) {
1897 this->write("_globals.");
1898 this->write(fInterfaceBlockNameMap[&f.base()->type()]);
1899 this->write("->");
1900 }
1901 this->writeName(field->fName);
1902 }
1903}
1904
1906 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1907 this->write(".");
1908 this->write(Swizzle::MaskString(swizzle.components()));
1909}
1910
1912 const Type& result) {
1913 SkASSERT(left.isMatrix());
1914 SkASSERT(right.isMatrix());
1915 SkASSERT(result.isMatrix());
1916
1917 std::string key = "Matrix *= " + this->typeName(left) + ":" + this->typeName(right);
1918
1919 if (!fHelpers.contains(key)) {
1920 fHelpers.add(key);
1921 fExtraFunctions.printf("thread %s& operator*=(thread %s& left, thread const %s& right) {\n"
1922 " left = left * right;\n"
1923 " return left;\n"
1924 "}\n",
1925 this->typeName(result).c_str(), this->typeName(left).c_str(),
1926 this->typeName(right).c_str());
1927 }
1928}
1929
1931 SkASSERT(left.isMatrix());
1932 SkASSERT(right.isMatrix());
1933 SkASSERT(left.rows() == right.rows());
1934 SkASSERT(left.columns() == right.columns());
1935
1936 std::string key = "Matrix == " + this->typeName(left) + ":" + this->typeName(right);
1937
1938 if (!fHelpers.contains(key)) {
1939 fHelpers.add(key);
1941thread bool operator==(const %s left, const %s right);
1942thread bool operator!=(const %s left, const %s right);
1943)",
1944 this->typeName(left).c_str(),
1945 this->typeName(right).c_str(),
1946 this->typeName(left).c_str(),
1947 this->typeName(right).c_str());
1948
1950 "thread bool operator==(const %s left, const %s right) {\n"
1951 " return ",
1952 this->typeName(left).c_str(), this->typeName(right).c_str());
1953
1954 const char* separator = "";
1955 for (int index=0; index<left.columns(); ++index) {
1956 fExtraFunctions.printf("%sall(left[%d] == right[%d])", separator, index, index);
1957 separator = " &&\n ";
1958 }
1959
1961 ";\n"
1962 "}\n"
1963 "thread bool operator!=(const %s left, const %s right) {\n"
1964 " return !(left == right);\n"
1965 "}\n",
1966 this->typeName(left).c_str(), this->typeName(right).c_str());
1967 }
1968}
1969
1971 SkASSERT(type.isMatrix());
1972
1973 std::string key = "Matrix / " + this->typeName(type);
1974
1975 if (!fHelpers.contains(key)) {
1976 fHelpers.add(key);
1977 std::string typeName = this->typeName(type);
1978
1980 "thread %s operator/(const %s left, const %s right) {\n"
1981 " return %s(",
1982 typeName.c_str(), typeName.c_str(), typeName.c_str(), typeName.c_str());
1983
1984 const char* separator = "";
1985 for (int index=0; index<type.columns(); ++index) {
1986 fExtraFunctions.printf("%sleft[%d] / right[%d]", separator, index, index);
1987 separator = ", ";
1988 }
1989
1990 fExtraFunctions.printf(");\n"
1991 "}\n"
1992 "thread %s& operator/=(thread %s& left, thread const %s& right) {\n"
1993 " left = left / right;\n"
1994 " return left;\n"
1995 "}\n",
1996 typeName.c_str(), typeName.c_str(), typeName.c_str());
1997 }
1998}
1999
2001 SkASSERT(type.isArray());
2002
2003 // If the array's component type needs a helper as well, we need to emit that one first.
2004 this->writeEqualityHelpers(type.componentType(), type.componentType());
2005
2006 std::string key = "ArrayEquality []";
2007 if (!fHelpers.contains(key)) {
2008 fHelpers.add(key);
2010template <typename T1, typename T2>
2011bool operator==(const array_ref<T1> left, const array_ref<T2> right);
2012template <typename T1, typename T2>
2013bool operator!=(const array_ref<T1> left, const array_ref<T2> right);
2014)");
2016template <typename T1, typename T2>
2017bool operator==(const array_ref<T1> left, const array_ref<T2> right) {
2018 if (left.size() != right.size()) {
2019 return false;
2020 }
2021 for (size_t index = 0; index < left.size(); ++index) {
2022 if (!all(left[index] == right[index])) {
2023 return false;
2024 }
2025 }
2026 return true;
2027}
2028
2029template <typename T1, typename T2>
2030bool operator!=(const array_ref<T1> left, const array_ref<T2> right) {
2031 return !(left == right);
2032}
2033)");
2034 }
2035}
2036
2038 SkASSERT(type.isStruct());
2039 std::string key = "StructEquality " + this->typeName(type);
2040
2041 if (!fHelpers.contains(key)) {
2042 fHelpers.add(key);
2043 // If one of the struct's fields needs a helper as well, we need to emit that one first.
2044 for (const Field& field : type.fields()) {
2045 this->writeEqualityHelpers(*field.fType, *field.fType);
2046 }
2047
2048 // Write operator== and operator!= for this struct, since those are assumed to exist in SkSL
2049 // and GLSL but do not exist by default in Metal.
2051thread bool operator==(thread const %s& left, thread const %s& right);
2052thread bool operator!=(thread const %s& left, thread const %s& right);
2053)",
2054 this->typeName(type).c_str(),
2055 this->typeName(type).c_str(),
2056 this->typeName(type).c_str(),
2057 this->typeName(type).c_str());
2058
2060 "thread bool operator==(thread const %s& left, thread const %s& right) {\n"
2061 " return ",
2062 this->typeName(type).c_str(),
2063 this->typeName(type).c_str());
2064
2065 const char* separator = "";
2066 for (const Field& field : type.fields()) {
2067 if (field.fType->isArray()) {
2069 "%s(make_array_ref(left.%.*s) == make_array_ref(right.%.*s))",
2070 separator,
2071 (int)field.fName.size(), field.fName.data(),
2072 (int)field.fName.size(), field.fName.data());
2073 } else {
2074 fExtraFunctions.printf("%sall(left.%.*s == right.%.*s)",
2075 separator,
2076 (int)field.fName.size(), field.fName.data(),
2077 (int)field.fName.size(), field.fName.data());
2078 }
2079 separator = " &&\n ";
2080 }
2082 ";\n"
2083 "}\n"
2084 "thread bool operator!=(thread const %s& left, thread const %s& right) {\n"
2085 " return !(left == right);\n"
2086 "}\n",
2087 this->typeName(type).c_str(),
2088 this->typeName(type).c_str());
2089 }
2090}
2091
2092void MetalCodeGenerator::writeEqualityHelpers(const Type& leftType, const Type& rightType) {
2093 if (leftType.isArray() && rightType.isArray()) {
2094 this->writeArrayEqualityHelpers(leftType);
2095 return;
2096 }
2097 if (leftType.isStruct() && rightType.isStruct()) {
2098 this->writeStructEqualityHelpers(leftType);
2099 return;
2100 }
2101 if (leftType.isMatrix() && rightType.isMatrix()) {
2102 this->writeMatrixEqualityHelpers(leftType, rightType);
2103 return;
2104 }
2105}
2106
2108 std::string str = this->typeName(type) + '(';
2109
2110 auto separator = SkSL::String::Separator();
2111 for (int index = type.slotCount(); index--;) {
2112 str += separator();
2113 str += "1.0";
2114 }
2115
2116 return str + ')';
2117}
2118
2119void MetalCodeGenerator::writeNumberAsMatrix(const Expression& expr, const Type& matrixType) {
2120 SkASSERT(expr.type().isNumber());
2121 SkASSERT(matrixType.isMatrix());
2122
2123 // Componentwise multiply the scalar against a matrix of the desired size which contains all 1s.
2124 this->write("(");
2125 this->write(this->splatMatrixOf1(matrixType));
2126 this->write(" * ");
2127 this->writeExpression(expr, Precedence::kMultiplicative);
2128 this->write(")");
2129}
2130
2132 Operator op,
2133 const Expression& other,
2134 Precedence precedence) {
2135 bool needMatrixSplatOnScalar = other.type().isMatrix() && expr.type().isNumber() &&
2137 op.removeAssignment().kind() != Operator::Kind::STAR;
2138 if (needMatrixSplatOnScalar) {
2139 this->writeNumberAsMatrix(expr, other.type());
2140 } else if (op.isEquality() && expr.type().isArray()) {
2141 this->write("make_array_ref(");
2142 this->writeExpression(expr, precedence);
2143 this->write(")");
2144 } else {
2145 this->writeExpression(expr, precedence);
2146 }
2147}
2148
2150 Precedence parentPrecedence) {
2151 const Expression& left = *b.left();
2152 const Expression& right = *b.right();
2153 const Type& leftType = left.type();
2154 const Type& rightType = right.type();
2155 Operator op = b.getOperator();
2156 Precedence precedence = op.getBinaryPrecedence();
2157 bool needParens = precedence >= parentPrecedence;
2158 switch (op.kind()) {
2159 case Operator::Kind::EQEQ:
2160 this->writeEqualityHelpers(leftType, rightType);
2161 if (leftType.isVector()) {
2162 this->write("all");
2163 needParens = true;
2164 }
2165 break;
2166 case Operator::Kind::NEQ:
2167 this->writeEqualityHelpers(leftType, rightType);
2168 if (leftType.isVector()) {
2169 this->write("any");
2170 needParens = true;
2171 }
2172 break;
2173 default:
2174 break;
2175 }
2176 if (leftType.isMatrix() && rightType.isMatrix() && op.kind() == Operator::Kind::STAREQ) {
2177 this->writeMatrixTimesEqualHelper(leftType, rightType, b.type());
2178 }
2179 if (op.removeAssignment().kind() == Operator::Kind::SLASH &&
2180 ((leftType.isMatrix() && rightType.isMatrix()) ||
2181 (leftType.isScalar() && rightType.isMatrix()) ||
2182 (leftType.isMatrix() && rightType.isScalar()))) {
2183 this->writeMatrixDivisionHelpers(leftType.isMatrix() ? leftType : rightType);
2184 }
2185
2186 if (needParens) {
2187 this->write("(");
2188 }
2189
2190 // Some expressions need to be rewritten from `lhs *= rhs` to `lhs = lhs * rhs`, e.g.:
2191 // float4 x = float4(1);
2192 // x.xy *= float2x2(...);
2193 // will report the error "non-const reference cannot bind to vector element."
2194 if (op.isCompoundAssignment() && left.kind() == Expression::Kind::kSwizzle) {
2195 // We need to do the rewrite. This could be dangerous if the lhs contains an index
2196 // expression with a side effect (such as `array[Func()]`), so we enable index-substitution
2197 // here for the LHS; any index-expression with side effects will be evaluated into a scratch
2198 // variable.
2199 this->writeWithIndexSubstitution([&] {
2200 this->writeExpression(left, precedence);
2201 this->write(" = ");
2202 this->writeExpression(left, Precedence::kAssignment);
2204
2205 // We never want to create index-expression substitutes on the RHS of the expression;
2206 // the RHS is only emitted one time.
2207 fIndexSubstitutionData->fCreateSubstitutes = false;
2208
2209 this->writeBinaryExpressionElement(right, op, left,
2211 });
2212 } else {
2213 // We don't need any rewrite; emit the binary expression as-is.
2214 this->writeBinaryExpressionElement(left, op, right, precedence);
2215 this->write(operator_name(op));
2216 this->writeBinaryExpressionElement(right, op, left, precedence);
2217 }
2218
2219 if (needParens) {
2220 this->write(")");
2221 }
2222}
2223
2225 Precedence parentPrecedence) {
2226 if (Precedence::kTernary >= parentPrecedence) {
2227 this->write("(");
2228 }
2229 this->writeExpression(*t.test(), Precedence::kTernary);
2230 this->write(" ? ");
2231 this->writeExpression(*t.ifTrue(), Precedence::kTernary);
2232 this->write(" : ");
2233 this->writeExpression(*t.ifFalse(), Precedence::kTernary);
2234 if (Precedence::kTernary >= parentPrecedence) {
2235 this->write(")");
2236 }
2237}
2238
2240 Precedence parentPrecedence) {
2241 const Operator op = p.getOperator();
2242 switch (op.kind()) {
2244 // According to the MSL specification, the arithmetic unary operators (+ and –) do not
2245 // act upon matrix-typed operands. We treat the unary "+" as a no-op for all operands.
2246 this->writeExpression(*p.operand(), Precedence::kPrefix);
2247 return;
2248
2249 case Operator::Kind::MINUS:
2250 // Transform the unary `-` on a matrix type to a multiplication by -1.
2251 if (p.operand()->type().isMatrix()) {
2252 this->write(p.type().componentType().highPrecision() ? "(-1.0 * "
2253 : "(-1.0h * ");
2254 this->writeExpression(*p.operand(), Precedence::kMultiplicative);
2255 this->write(")");
2256 return;
2257 }
2258 break;
2259
2260 case Operator::Kind::PLUSPLUS:
2261 case Operator::Kind::MINUSMINUS:
2262 if (p.operand()->type().isMatrix()) {
2263 // Transform `++x` or `--x` on a matrix type to `mat += T(1.0, ...)` or
2264 // `mat -= T(1.0, ...)`.
2265 this->write("(");
2266 this->writeExpression(*p.operand(), Precedence::kAssignment);
2267 this->write(op.kind() == Operator::Kind::PLUSPLUS ? " += " : " -= ");
2268 this->write(this->splatMatrixOf1(p.operand()->type()));
2269 this->write(")");
2270 return;
2271 }
2272 break;
2273
2274 default:
2275 break;
2276 }
2277
2278 if (Precedence::kPrefix >= parentPrecedence) {
2279 this->write("(");
2280 }
2281
2282 this->write(op.tightOperatorName());
2283 this->writeExpression(*p.operand(), Precedence::kPrefix);
2284
2285 if (Precedence::kPrefix >= parentPrecedence) {
2286 this->write(")");
2287 }
2288}
2289
2291 Precedence parentPrecedence) {
2292 const Operator op = p.getOperator();
2293 switch (op.kind()) {
2294 case Operator::Kind::PLUSPLUS:
2295 case Operator::Kind::MINUSMINUS:
2296 if (p.operand()->type().isMatrix()) {
2297 // We need to transform `x++` or `x--` into `+=` and `-=` on a matrix.
2298 // Unfortunately, that requires making a temporary copy of the old value and
2299 // emitting a sequence expression: `((temp = mat), (mat += T(1.0, ...)), temp)`.
2300 std::string tempMatrix = this->getTempVariable(p.operand()->type());
2301 this->write("((");
2302 this->write(tempMatrix);
2303 this->write(" = ");
2304 this->writeExpression(*p.operand(), Precedence::kAssignment);
2305 this->write("), (");
2306 this->writeExpression(*p.operand(), Precedence::kAssignment);
2307 this->write(op.kind() == Operator::Kind::PLUSPLUS ? " += " : " -= ");
2308 this->write(this->splatMatrixOf1(p.operand()->type()));
2309 this->write("), ");
2310 this->write(tempMatrix);
2311 this->write(")");
2312 return;
2313 }
2314 break;
2315
2316 default:
2317 break;
2318 }
2319
2320 if (Precedence::kPostfix >= parentPrecedence) {
2321 this->write("(");
2322 }
2323 this->writeExpression(*p.operand(), Precedence::kPostfix);
2324 this->write(op.tightOperatorName());
2325 if (Precedence::kPostfix >= parentPrecedence) {
2326 this->write(")");
2327 }
2328}
2329
2331 const Type& type = l.type();
2332 if (type.isFloat()) {
2334 if (!l.type().highPrecision()) {
2335 this->write("h");
2336 }
2337 return;
2338 }
2339 if (type.isInteger()) {
2340 if (type.matches(*fContext.fTypes.fUInt)) {
2341 this->write(std::to_string(l.intValue() & 0xffffffff));
2342 this->write("u");
2343 } else if (type.matches(*fContext.fTypes.fUShort)) {
2344 this->write(std::to_string(l.intValue() & 0xffff));
2345 this->write("u");
2346 } else {
2347 this->write(std::to_string(l.intValue()));
2348 }
2349 return;
2350 }
2351 SkASSERT(type.isBoolean());
2353}
2354
2356 const char*& separator) {
2359 this->write(separator);
2360 this->write("_in");
2361 separator = ", ";
2362 }
2364 this->write(separator);
2365 this->write("_out");
2366 separator = ", ";
2367 }
2369 this->write(separator);
2370 this->write("_uniforms");
2371 separator = ", ";
2372 }
2374 this->write(separator);
2375 this->write("_globals");
2376 separator = ", ";
2377 }
2379 this->write(separator);
2380 this->write("_fragCoord");
2381 separator = ", ";
2382 }
2384 this->write(separator);
2385 this->write("sk_SampleMaskIn");
2386 separator = ", ";
2387 }
2389 this->write(separator);
2390 this->write("sk_VertexID");
2391 separator = ", ";
2392 }
2394 this->write(separator);
2395 this->write("sk_InstanceID");
2396 separator = ", ";
2397 }
2399 this->write(separator);
2400 this->write("_threadgroups");
2401 separator = ", ";
2402 }
2403}
2404
2406 const char*& separator) {
2409 this->write(separator);
2410 this->write("Inputs _in");
2411 separator = ", ";
2412 }
2414 this->write(separator);
2415 this->write("thread Outputs& _out");
2416 separator = ", ";
2417 }
2419 this->write(separator);
2420 this->write("Uniforms _uniforms");
2421 separator = ", ";
2422 }
2424 this->write(separator);
2425 this->write("thread Globals& _globals");
2426 separator = ", ";
2427 }
2429 this->write(separator);
2430 this->write("float4 _fragCoord");
2431 separator = ", ";
2432 }
2434 this->write(separator);
2435 this->write("uint sk_SampleMaskIn");
2436 separator = ", ";
2437 }
2439 this->write(separator);
2440 this->write("uint sk_VertexID");
2441 separator = ", ";
2442 }
2444 this->write(separator);
2445 this->write("uint sk_InstanceID");
2446 separator = ", ";
2447 }
2449 this->write(separator);
2450 this->write("threadgroup Threadgroups& _threadgroups");
2451 separator = ", ";
2452 }
2453}
2454
2456 return (layout.fBinding >= 0) ? layout.fBinding
2457 : fProgram.fConfig->fSettings.fDefaultUniformBinding;
2458}
2459
2461 return (layout.fSet >= 0) ? layout.fSet
2462 : fProgram.fConfig->fSettings.fDefaultUniformSet;
2463}
2464
2467 ? "_globals._anonInterface0->" SKSL_RTFLIP_NAME
2468 : "";
2469 const char* separator = "";
2470 if (f.isMain()) {
2472 this->write("fragment Outputs fragmentMain(");
2473 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2474 this->write("vertex Outputs vertexMain(");
2475 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2476 this->write("kernel void computeMain(");
2477 } else {
2478 fContext.fErrors->error(Position(), "unsupported kind of program");
2479 return false;
2480 }
2482 this->write("Inputs _in [[stage_in]]");
2483 separator = ", ";
2484 }
2485 if (-1 != fUniformBuffer) {
2486 this->write(separator);
2487 this->write("constant Uniforms& _uniforms [[buffer(" +
2489 separator = ", ";
2490 }
2491 for (const ProgramElement* e : fProgram.elements()) {
2492 if (e->is<GlobalVarDeclaration>()) {
2493 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2494 const VarDeclaration& decl = decls.varDeclaration();
2495 const Variable* var = decl.var();
2496 const SkSL::Type::TypeKind varKind = var->type().typeKind();
2497
2498 if (varKind == Type::TypeKind::kSampler || varKind == Type::TypeKind::kTexture) {
2499 if (var->type().dimensions() != SpvDim2D) {
2500 // Not yet implemented--Skia currently only uses 2D textures.
2501 fContext.fErrors->error(decls.fPosition, "Unsupported texture dimensions");
2502 return false;
2503 }
2504
2505 int binding = getUniformBinding(var->layout());
2506 this->write(separator);
2507 separator = ", ";
2508
2509 if (varKind == Type::TypeKind::kSampler) {
2510 this->writeType(var->type().textureType());
2511 this->write(" ");
2512 this->writeName(var->mangledName());
2513 this->write(kTextureSuffix);
2514 this->write(" [[texture(");
2515 this->write(std::to_string(binding));
2516 this->write(")]], sampler ");
2517 this->writeName(var->mangledName());
2518 this->write(kSamplerSuffix);
2519 this->write(" [[sampler(");
2520 this->write(std::to_string(binding));
2521 this->write(")]]");
2522 } else {
2524 this->writeType(var->type());
2525 this->write(" ");
2526 this->writeName(var->mangledName());
2527 this->write(" [[texture(");
2528 this->write(std::to_string(binding));
2529 this->write(")]]");
2530 }
2531 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2532 std::string_view attr;
2533 switch (var->layout().fBuiltin) {
2535 attr = " [[threadgroups_per_grid]]";
2536 break;
2538 attr = " [[threadgroup_position_in_grid]]";
2539 break;
2541 attr = " [[thread_position_in_threadgroup]]";
2542 break;
2544 attr = " [[thread_position_in_grid]]";
2545 break;
2547 attr = " [[thread_index_in_threadgroup]]";
2548 break;
2549 default:
2550 break;
2551 }
2552 if (!attr.empty()) {
2553 this->write(separator);
2554 this->writeType(var->type());
2555 this->write(" ");
2556 this->write(var->name());
2557 this->write(attr);
2558 separator = ", ";
2559 }
2560 }
2561 } else if (e->is<InterfaceBlock>()) {
2562 const InterfaceBlock& intf = e->as<InterfaceBlock>();
2563 if (intf.typeName() == "sk_PerVertex") {
2564 continue;
2565 }
2566 this->write(separator);
2567 if (is_readonly(intf)) {
2568 this->write("const ");
2569 }
2570 this->write(is_buffer(intf) ? "device " : "constant ");
2571 this->writeType(intf.var()->type());
2572 this->write("& " );
2573 this->write(fInterfaceBlockNameMap[&intf.var()->type()]);
2574 this->write(" [[buffer(");
2575 this->write(std::to_string(this->getUniformBinding(intf.var()->layout())));
2576 this->write(")]]");
2577 separator = ", ";
2578 }
2579 }
2582 fInterfaceBlockNameMap.empty()) {
2583 this->write(separator);
2584 this->write("constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
2585 fRTFlipName = "_anonInterface0." SKSL_RTFLIP_NAME;
2586 separator = ", ";
2587 }
2588 this->write(separator);
2589 this->write("bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]");
2590 if (this->requirements(f) & kSampleMaskIn_Requirement) {
2591 this->write(", uint sk_SampleMaskIn [[sample_mask]]");
2592 }
2594 this->write(", half4 " + std::string(fCaps.fFBFetchColorName) +
2595 " [[color(0)]]\n");
2596 }
2597 separator = ", ";
2598 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2599 this->write(separator);
2600 this->write("uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
2601 separator = ", ";
2602 }
2603 } else {
2604 this->writeType(f.returnType());
2605 this->write(" ");
2606 this->writeName(f.mangledName());
2607 this->write("(");
2608 this->writeFunctionRequirementParams(f, separator);
2609 }
2610 for (const Variable* param : f.parameters()) {
2611 // This is a workaround for our test files. They use the runtime effect signature, so main
2612 // takes a coords parameter. We detect these at IR generation time, and we omit them from
2613 // the declaration here, so the function is valid Metal. (Well, valid as long as the
2614 // coordinates aren't actually referenced.)
2615 if (f.isMain() && param == f.getMainCoordsParameter()) {
2616 continue;
2617 }
2618 this->write(separator);
2619 separator = ", ";
2620 this->writeModifiers(param->modifierFlags());
2621 this->writeType(param->type());
2622 if (pass_by_reference(param->type(), param->modifierFlags())) {
2623 this->write("&");
2624 }
2625 this->write(" ");
2626 this->writeName(param->mangledName());
2627 }
2628 this->write(")");
2629 return true;
2630}
2631
2634 this->writeLine(";");
2635}
2636
2637static bool is_block_ending_with_return(const Statement* stmt) {
2638 // This function detects (potentially nested) blocks that end in a return statement.
2639 if (!stmt->is<Block>()) {
2640 return false;
2641 }
2642 const StatementArray& block = stmt->as<Block>().children();
2643 for (int index = block.size(); index--; ) {
2644 stmt = block[index].get();
2645 if (stmt->is<ReturnStatement>()) {
2646 return true;
2647 }
2648 if (stmt->is<Block>()) {
2649 return is_block_ending_with_return(stmt);
2650 }
2651 if (!stmt->is<Nop>()) {
2652 break;
2653 }
2654 }
2655 return false;
2656}
2657
2659 // Compute shaders only have input variables (e.g. sk_GlobalInvocationID) and access program
2660 // inputs/outputs via the Globals and Uniforms structs. We collect the allowed "in" parameters
2661 // into an Input struct here, since the rest of the code expects the normal _in / _out pattern.
2662 this->write("Inputs _in = { ");
2663 const char* separator = "";
2664 for (const ProgramElement* e : fProgram.elements()) {
2665 if (e->is<GlobalVarDeclaration>()) {
2666 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2667 const Variable* var = decls.varDeclaration().var();
2668 if (is_input(*var)) {
2669 this->write(separator);
2670 separator = ", ";
2671 this->writeName(var->mangledName());
2672 }
2673 }
2674 }
2675 this->writeLine(" };");
2676}
2677
2679 SkASSERT(!fProgram.fConfig->fSettings.fFragColorIsInOut);
2680
2681 if (!this->writeFunctionDeclaration(f.declaration())) {
2682 return;
2683 }
2684
2685 fCurrentFunction = &f.declaration();
2686 SkScopeExit clearCurrentFunction([&] { fCurrentFunction = nullptr; });
2687
2688 this->writeLine(" {");
2689
2690 if (f.declaration().isMain()) {
2691 fIndentation++;
2692 this->writeGlobalInit();
2694 this->writeThreadgroupInit();
2695 this->writeComputeMainInputs();
2696 }
2697 else {
2698 this->writeLine("Outputs _out;");
2699 this->writeLine("(void)_out;");
2700 }
2701 fIndentation--;
2702 }
2703
2704 fFunctionHeader.clear();
2706 {
2707 AutoOutputStream outputToBuffer(this, &buffer);
2708 fIndentation++;
2709 for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
2710 if (!stmt->isEmpty()) {
2711 this->writeStatement(*stmt);
2712 this->finishLine();
2713 }
2714 }
2715 if (f.declaration().isMain()) {
2716 // If the main function doesn't end with a return, we need to synthesize one here.
2717 if (!is_block_ending_with_return(f.body().get())) {
2719 this->finishLine();
2720 }
2721 }
2722 fIndentation--;
2723 this->writeLine("}");
2724 }
2725 this->write(fFunctionHeader);
2726 this->write(buffer.str());
2727}
2728
2732 this->write("device ");
2733 } else if (flags & ModifierFlag::kOut) {
2734 this->write("thread ");
2735 }
2736 if (flags.isConst()) {
2737 this->write("const ");
2738 }
2739}
2740
2742 if (intf.typeName() == "sk_PerVertex") {
2743 return;
2744 }
2745 const Type* structType = &intf.var()->type().componentType();
2746 this->writeModifiers(intf.var()->modifierFlags());
2747 this->write("struct ");
2748 this->writeType(*structType);
2749 this->writeLine(" {");
2750 fIndentation++;
2751 this->writeFields(structType->fields(), structType->fPosition);
2753 this->writeLine("float2 " SKSL_RTFLIP_NAME ";");
2754 }
2755 fIndentation--;
2756 this->write("}");
2757 if (!intf.instanceName().empty()) {
2758 this->write(" ");
2759 this->write(intf.instanceName());
2760 if (intf.arraySize() > 0) {
2761 this->write("[");
2762 this->write(std::to_string(intf.arraySize()));
2763 this->write("]");
2764 }
2765 fInterfaceBlockNameMap.set(&intf.var()->type(), std::string(intf.instanceName()));
2766 } else {
2767 fInterfaceBlockNameMap.set(&intf.var()->type(),
2768 "_anonInterface" + std::to_string(fAnonInterfaceCount++));
2769 }
2770 this->writeLine(";");
2771}
2772
2775 int currentOffset = 0;
2776 for (const Field& field : fields) {
2777 int fieldOffset = field.fLayout.fOffset;
2778 const Type* fieldType = field.fType;
2779 if (!memoryLayout.isSupported(*fieldType)) {
2780 fContext.fErrors->error(parentPos, "type '" + std::string(fieldType->name()) +
2781 "' is not permitted here");
2782 return;
2783 }
2784 if (fieldOffset != -1) {
2785 if (currentOffset > fieldOffset) {
2786 fContext.fErrors->error(field.fPosition,
2787 "offset of field '" + std::string(field.fName) +
2788 "' must be at least " + std::to_string(currentOffset));
2789 return;
2790 } else if (currentOffset < fieldOffset) {
2791 this->write("char pad");
2793 this->write("[");
2794 this->write(std::to_string(fieldOffset - currentOffset));
2795 this->writeLine("];");
2796 currentOffset = fieldOffset;
2797 }
2798 int alignment = memoryLayout.alignment(*fieldType);
2799 if (fieldOffset % alignment) {
2800 fContext.fErrors->error(field.fPosition,
2801 "offset of field '" + std::string(field.fName) +
2802 "' must be a multiple of " + std::to_string(alignment));
2803 return;
2804 }
2805 }
2806 if (fieldType->isUnsizedArray()) {
2807 // An unsized array always appears as the last member of a storage block. We declare
2808 // it as a one-element array and allow dereferencing past the capacity.
2809 // TODO(armansito): This is because C++ does not support flexible array members like C99
2810 // does. This generally works but it can lead to UB as compilers are free to insert
2811 // padding past the first element of the array. An alternative approach is to declare
2812 // the struct without the unsized array member and replace variable references with a
2813 // buffer offset calculation based on sizeof().
2814 this->writeModifiers(field.fModifierFlags);
2815 this->writeType(fieldType->componentType());
2816 this->write(" ");
2817 this->writeName(field.fName);
2818 this->write("[1]");
2819 } else {
2820 size_t fieldSize = memoryLayout.size(*fieldType);
2821 if (fieldSize > static_cast<size_t>(std::numeric_limits<int>::max() - currentOffset)) {
2822 fContext.fErrors->error(parentPos, "field offset overflow");
2823 return;
2824 }
2825 currentOffset += fieldSize;
2826 this->writeModifiers(field.fModifierFlags);
2827 this->writeType(*fieldType);
2828 this->write(" ");
2829 this->writeName(field.fName);
2830 }
2831 this->writeLine(";");
2832 }
2833}
2834
2836 this->writeExpression(value, Precedence::kExpression);
2837}
2838
2839void MetalCodeGenerator::writeName(std::string_view name) {
2841 this->write("_"); // adding underscore before name to avoid conflict with reserved words
2842 }
2843 this->write(name);
2844}
2845
2847 this->writeModifiers(varDecl.var()->modifierFlags());
2848 this->writeType(varDecl.var()->type());
2849 this->write(" ");
2850 this->writeName(varDecl.var()->mangledName());
2851 if (varDecl.value()) {
2852 this->write(" = ");
2853 this->writeVarInitializer(*varDecl.var(), *varDecl.value());
2854 }
2855 this->write(";");
2856}
2857
2859 switch (s.kind()) {
2860 case Statement::Kind::kBlock:
2861 this->writeBlock(s.as<Block>());
2862 break;
2863 case Statement::Kind::kExpression:
2865 break;
2866 case Statement::Kind::kReturn:
2868 break;
2869 case Statement::Kind::kVarDeclaration:
2871 break;
2872 case Statement::Kind::kIf:
2873 this->writeIfStatement(s.as<IfStatement>());
2874 break;
2875 case Statement::Kind::kFor:
2876 this->writeForStatement(s.as<ForStatement>());
2877 break;
2878 case Statement::Kind::kDo:
2879 this->writeDoStatement(s.as<DoStatement>());
2880 break;
2881 case Statement::Kind::kSwitch:
2883 break;
2884 case Statement::Kind::kBreak:
2885 this->write("break;");
2886 break;
2888 this->write("continue;");
2889 break;
2891 this->write("discard_fragment();");
2892 break;
2894 this->write(";");
2895 break;
2896 default:
2897 SkDEBUGFAILF("unsupported statement: %s", s.description().c_str());
2898 break;
2899 }
2900}
2901
2903 // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
2904 // something here to make the code valid).
2905 bool isScope = b.isScope() || b.isEmpty();
2906 if (isScope) {
2907 this->writeLine("{");
2908 fIndentation++;
2909 }
2910 for (const std::unique_ptr<Statement>& stmt : b.children()) {
2911 if (!stmt->isEmpty()) {
2912 this->writeStatement(*stmt);
2913 this->finishLine();
2914 }
2915 }
2916 if (isScope) {
2917 fIndentation--;
2918 this->write("}");
2919 }
2920}
2921
2923 this->write("if (");
2924 this->writeExpression(*stmt.test(), Precedence::kExpression);
2925 this->write(") ");
2926 this->writeStatement(*stmt.ifTrue());
2927 if (stmt.ifFalse()) {
2928 this->write(" else ");
2929 this->writeStatement(*stmt.ifFalse());
2930 }
2931}
2932
2934 // Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
2935 if (!f.initializer() && f.test() && !f.next()) {
2936 this->write("while (");
2937 this->writeExpression(*f.test(), Precedence::kExpression);
2938 this->write(") ");
2939 this->writeStatement(*f.statement());
2940 return;
2941 }
2942
2943 this->write("for (");
2944 if (f.initializer() && !f.initializer()->isEmpty()) {
2945 this->writeStatement(*f.initializer());
2946 } else {
2947 this->write("; ");
2948 }
2949 if (f.test()) {
2950 this->writeExpression(*f.test(), Precedence::kExpression);
2951 }
2952 this->write("; ");
2953 if (f.next()) {
2954 this->writeExpression(*f.next(), Precedence::kExpression);
2955 }
2956 this->write(") ");
2957 this->writeStatement(*f.statement());
2958}
2959
2961 this->write("do ");
2962 this->writeStatement(*d.statement());
2963 this->write(" while (");
2964 this->writeExpression(*d.test(), Precedence::kExpression);
2965 this->write(");");
2966}
2967
2969 if (fProgram.fConfig->fSettings.fOptimize && !Analysis::HasSideEffects(*s.expression())) {
2970 // Don't emit dead expressions.
2971 return;
2972 }
2973 this->writeExpression(*s.expression(), Precedence::kStatement);
2974 this->write(";");
2975}
2976
2978 this->write("switch (");
2979 this->writeExpression(*s.value(), Precedence::kExpression);
2980 this->writeLine(") {");
2981 fIndentation++;
2982 for (const std::unique_ptr<Statement>& stmt : s.cases()) {
2983 const SwitchCase& c = stmt->as<SwitchCase>();
2984 if (c.isDefault()) {
2985 this->writeLine("default:");
2986 } else {
2987 this->write("case ");
2988 this->write(std::to_string(c.value()));
2989 this->writeLine(":");
2990 }
2991 if (!c.statement()->isEmpty()) {
2992 fIndentation++;
2993 this->writeStatement(*c.statement());
2994 this->finishLine();
2995 fIndentation--;
2996 }
2997 }
2998 fIndentation--;
2999 this->write("}");
3000}
3001
3003 // main functions in Metal return a magic _out parameter that doesn't exist in SkSL.
3006 this->write("return _out;");
3007 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
3008 this->write("return;");
3009 } else {
3010 SkDEBUGFAIL("unsupported kind of program");
3011 }
3012}
3013
3016 if (r.expression()) {
3017 if (r.expression()->type().matches(*fContext.fTypes.fHalf4)) {
3018 this->write("_out.sk_FragColor = ");
3019 this->writeExpression(*r.expression(), Precedence::kExpression);
3020 this->writeLine(";");
3021 } else {
3023 "Metal does not support returning '" +
3024 r.expression()->type().description() + "' from main()");
3025 }
3026 }
3028 return;
3029 }
3030
3031 this->write("return");
3032 if (r.expression()) {
3033 this->write(" ");
3034 this->writeExpression(*r.expression(), Precedence::kExpression);
3035 }
3036 this->write(";");
3037}
3038
3040 this->writeLine("#include <metal_stdlib>");
3041 this->writeLine("#include <simd/simd.h>");
3042 this->writeLine("#ifdef __clang__");
3043 this->writeLine("#pragma clang diagnostic ignored \"-Wall\"");
3044 this->writeLine("#endif");
3045 this->writeLine("using namespace metal;");
3046}
3047
3049 class : public GlobalStructVisitor {
3050 public:
3051 void visitSampler(const Type&, std::string_view) override {
3052 if (fWrotePolyfill) {
3053 return;
3054 }
3055 fWrotePolyfill = true;
3056
3057 std::string polyfill = SkSL::String::printf(R"(
3058struct sampler2D {
3059 texture2d<half> tex;
3060 sampler smp;
3061};
3062half4 sample(sampler2D i, float2 p, float b=%g) { return i.tex.sample(i.smp, p, bias(b)); }
3063half4 sample(sampler2D i, float3 p, float b=%g) { return i.tex.sample(i.smp, p.xy / p.z, bias(b)); }
3064half4 sampleLod(sampler2D i, float2 p, float lod) { return i.tex.sample(i.smp, p, level(lod)); }
3065half4 sampleLod(sampler2D i, float3 p, float lod) {
3066 return i.tex.sample(i.smp, p.xy / p.z, level(lod));
3067}
3068half4 sampleGrad(sampler2D i, float2 p, float2 dPdx, float2 dPdy) {
3069 return i.tex.sample(i.smp, p, gradient2d(dPdx, dPdy));
3070}
3071
3072)",
3073 fTextureBias,
3074 fTextureBias);
3075 fCodeGen->write(polyfill.c_str());
3076 }
3077
3078 MetalCodeGenerator* fCodeGen = nullptr;
3079 float fTextureBias = 0.0f;
3080 bool fWrotePolyfill = false;
3081 } visitor;
3082
3083 visitor.fCodeGen = this;
3084 visitor.fTextureBias = fProgram.fConfig->fSettings.fSharpenTextures ? kSharpenTexturesBias
3085 : 0.0f;
3086 this->visitGlobalStruct(&visitor);
3087}
3088
3090 for (const ProgramElement* e : fProgram.elements()) {
3091 if (e->is<GlobalVarDeclaration>()) {
3092 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3093 const Variable& var = *decls.varDeclaration().var();
3094 if (var.modifierFlags().isUniform()) {
3095 SkASSERT(var.type().typeKind() != Type::TypeKind::kSampler &&
3096 var.type().typeKind() != Type::TypeKind::kTexture);
3097 int uniformSet = this->getUniformSet(var.layout());
3098 // Make sure that the program's uniform-set value is consistent throughout.
3099 if (-1 == fUniformBuffer) {
3100 this->write("struct Uniforms {\n");
3101 fUniformBuffer = uniformSet;
3102 } else if (uniformSet != fUniformBuffer) {
3104 "Metal backend requires all uniforms to have the same "
3105 "'layout(set=...)'");
3106 }
3107 this->write(" ");
3108 this->writeType(var.type());
3109 this->write(" ");
3110 this->writeName(var.mangledName());
3111 this->write(";\n");
3112 }
3113 }
3114 }
3115 if (-1 != fUniformBuffer) {
3116 this->write("};\n");
3117 }
3118}
3119
3121 this->write("struct Inputs {\n");
3122 for (const ProgramElement* e : fProgram.elements()) {
3123 if (e->is<GlobalVarDeclaration>()) {
3124 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3125 const Variable& var = *decls.varDeclaration().var();
3126 if (is_input(var)) {
3127 this->write(" ");
3129 needs_address_space(var.type(), var.modifierFlags())) {
3130 // TODO: address space support
3131 this->write("device ");
3132 }
3133 this->writeType(var.type());
3134 if (pass_by_reference(var.type(), var.modifierFlags())) {
3135 this->write("&");
3136 }
3137 this->write(" ");
3138 this->writeName(var.mangledName());
3139 if (-1 != var.layout().fLocation) {
3141 this->write(" [[attribute(" + std::to_string(var.layout().fLocation) +
3142 ")]]");
3143 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3144 this->write(" [[user(locn" + std::to_string(var.layout().fLocation) +
3145 ")]]");
3146 }
3147 }
3148 this->write(";\n");
3149 }
3150 }
3151 }
3152 this->write("};\n");
3153}
3154
3156 this->write("struct Outputs {\n");
3158 this->write(" float4 sk_Position [[position]];\n");
3159 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3160 this->write(" half4 sk_FragColor [[color(0)]];\n");
3162 this->write(" half4 sk_SecondaryFragColor [[color(0), index(1)]];\n");
3163 }
3164 }
3165 for (const ProgramElement* e : fProgram.elements()) {
3166 if (e->is<GlobalVarDeclaration>()) {
3167 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3168 const Variable& var = *decls.varDeclaration().var();
3169 if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3170 this->write(" uint sk_SampleMask [[sample_mask]];\n");
3171 continue;
3172 }
3173 if (is_output(var)) {
3174 this->write(" ");
3176 needs_address_space(var.type(), var.modifierFlags())) {
3177 // TODO: address space support
3178 this->write("device ");
3179 }
3180 this->writeType(var.type());
3182 pass_by_reference(var.type(), var.modifierFlags())) {
3183 this->write("&");
3184 }
3185 this->write(" ");
3186 this->writeName(var.mangledName());
3187
3188 int location = var.layout().fLocation;
3189 if (!ProgramConfig::IsCompute(fProgram.fConfig->fKind) && location < 0 &&
3190 var.type().typeKind() != Type::TypeKind::kTexture) {
3191 fContext.fErrors->error(var.fPosition,
3192 "Metal out variables must have 'layout(location=...)'");
3193 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
3194 this->write(" [[user(locn" + std::to_string(location) + ")]]");
3195 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3196 this->write(" [[color(" + std::to_string(location) + ")");
3197 int colorIndex = var.layout().fIndex;
3198 if (colorIndex) {
3199 this->write(", index(" + std::to_string(colorIndex) + ")");
3200 }
3201 this->write("]]");
3202 }
3203 this->write(";\n");
3204 }
3205 }
3206 }
3208 this->write(" float sk_PointSize [[point_size]];\n");
3209 }
3210 this->write("};\n");
3211}
3212
3214 bool wroteInterfaceBlock = false;
3215 for (const ProgramElement* e : fProgram.elements()) {
3216 if (e->is<InterfaceBlock>()) {
3217 this->writeInterfaceBlock(e->as<InterfaceBlock>());
3218 wroteInterfaceBlock = true;
3219 }
3220 }
3221 if (!wroteInterfaceBlock &&
3223 this->writeLine("struct sksl_synthetic_uniforms {");
3224 this->writeLine(" float2 " SKSL_RTFLIP_NAME ";");
3225 this->writeLine("};");
3226 }
3227}
3228
3230 for (const ProgramElement* e : fProgram.elements()) {
3231 if (e->is<StructDefinition>()) {
3233 }
3234 }
3235}
3236
3238 class : public GlobalStructVisitor {
3239 public:
3240 void visitConstantVariable(const VarDeclaration& decl) override {
3241 fCodeGen->write("constant ");
3242 fCodeGen->writeVarDeclaration(decl);
3243 fCodeGen->finishLine();
3244 }
3245
3246 MetalCodeGenerator* fCodeGen = nullptr;
3247 } visitor;
3248
3249 visitor.fCodeGen = this;
3250 this->visitGlobalStruct(&visitor);
3251}
3252
3254 for (const ProgramElement* element : fProgram.elements()) {
3255 if (element->is<InterfaceBlock>()) {
3256 const auto* ib = &element->as<InterfaceBlock>();
3257 if (ib->typeName() != "sk_PerVertex") {
3258 visitor->visitInterfaceBlock(*ib, fInterfaceBlockNameMap[&ib->var()->type()]);
3259 }
3260 continue;
3261 }
3262 if (!element->is<GlobalVarDeclaration>()) {
3263 continue;
3264 }
3265 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3266 const VarDeclaration& decl = global.varDeclaration();
3267 const Variable& var = *decl.var();
3268 if (decl.baseType().typeKind() == Type::TypeKind::kSampler) {
3269 visitor->visitSampler(var.type(), var.mangledName());
3270 continue;
3271 }
3272 if (decl.baseType().typeKind() == Type::TypeKind::kTexture) {
3273 visitor->visitTexture(var.type(), var.mangledName());
3274 continue;
3275 }
3276 if (!(var.modifierFlags() & ~ModifierFlag::kConst) && var.layout().fBuiltin == -1) {
3277 if (is_in_globals(var)) {
3278 // Visit a regular global variable.
3279 visitor->visitNonconstantVariable(var, decl.value().get());
3280 } else {
3281 // Visit a constant-expression variable.
3283 visitor->visitConstantVariable(decl);
3284 }
3285 }
3286 }
3287}
3288
3290 class : public GlobalStructVisitor {
3291 public:
3292 void visitInterfaceBlock(const InterfaceBlock& block,
3293 std::string_view blockName) override {
3294 this->addElement();
3295 fCodeGen->write(" ");
3296 if (is_readonly(block)) {
3297 fCodeGen->write("const ");
3298 }
3299 fCodeGen->write(is_buffer(block) ? "device " : "constant ");
3300 fCodeGen->write(block.typeName());
3301 fCodeGen->write("* ");
3302 fCodeGen->writeName(blockName);
3303 fCodeGen->write(";\n");
3304 }
3305 void visitTexture(const Type& type, std::string_view name) override {
3306 this->addElement();
3307 fCodeGen->write(" ");
3308 fCodeGen->writeType(type);
3309 fCodeGen->write(" ");
3310 fCodeGen->writeName(name);
3311 fCodeGen->write(";\n");
3312 }
3313 void visitSampler(const Type&, std::string_view name) override {
3314 this->addElement();
3315 fCodeGen->write(" sampler2D ");
3316 fCodeGen->writeName(name);
3317 fCodeGen->write(";\n");
3318 }
3319 void visitConstantVariable(const VarDeclaration& decl) override {
3320 // Constants aren't added to the global struct.
3321 }
3322 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3323 this->addElement();
3324 fCodeGen->write(" ");
3325 fCodeGen->writeModifiers(var.modifierFlags());
3326 fCodeGen->writeType(var.type());
3327 fCodeGen->write(" ");
3328 fCodeGen->writeName(var.mangledName());
3329 fCodeGen->write(";\n");
3330 }
3331 void addElement() {
3332 if (fFirst) {
3333 fCodeGen->write("struct Globals {\n");
3334 fFirst = false;
3335 }
3336 }
3337 void finish() {
3338 if (!fFirst) {
3339 fCodeGen->writeLine("};");
3340 fFirst = true;
3341 }
3342 }
3343
3344 MetalCodeGenerator* fCodeGen = nullptr;
3345 bool fFirst = true;
3346 } visitor;
3347
3348 visitor.fCodeGen = this;
3349 this->visitGlobalStruct(&visitor);
3350 visitor.finish();
3351}
3352
3354 class : public GlobalStructVisitor {
3355 public:
3356 void visitInterfaceBlock(const InterfaceBlock& blockType,
3357 std::string_view blockName) override {
3358 this->addElement();
3359 fCodeGen->write("&");
3360 fCodeGen->writeName(blockName);
3361 }
3362 void visitTexture(const Type&, std::string_view name) override {
3363 this->addElement();
3364 fCodeGen->writeName(name);
3365 }
3366 void visitSampler(const Type&, std::string_view name) override {
3367 this->addElement();
3368 fCodeGen->write("{");
3369 fCodeGen->writeName(name);
3370 fCodeGen->write(kTextureSuffix);
3371 fCodeGen->write(", ");
3372 fCodeGen->writeName(name);
3373 fCodeGen->write(kSamplerSuffix);
3374 fCodeGen->write("}");
3375 }
3376 void visitConstantVariable(const VarDeclaration& decl) override {
3377 // Constant-expression variables aren't put in the global struct.
3378 }
3379 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3380 this->addElement();
3381 if (value) {
3382 fCodeGen->writeVarInitializer(var, *value);
3383 } else {
3384 fCodeGen->write("{}");
3385 }
3386 }
3387 void addElement() {
3388 if (fFirst) {
3389 fCodeGen->write("Globals _globals{");
3390 fFirst = false;
3391 } else {
3392 fCodeGen->write(", ");
3393 }
3394 }
3395 void finish() {
3396 if (!fFirst) {
3397 fCodeGen->writeLine("};");
3398 fCodeGen->writeLine("(void)_globals;");
3399 }
3400 }
3401 MetalCodeGenerator* fCodeGen = nullptr;
3402 bool fFirst = true;
3403 } visitor;
3404
3405 visitor.fCodeGen = this;
3406 this->visitGlobalStruct(&visitor);
3407 visitor.finish();
3408}
3409
3411 for (const ProgramElement* element : fProgram.elements()) {
3412 if (!element->is<GlobalVarDeclaration>()) {
3413 continue;
3414 }
3415 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3416 const VarDeclaration& decl = global.varDeclaration();
3417 const Variable& var = *decl.var();
3418 if (var.modifierFlags().isWorkgroup()) {
3419 SkASSERT(!decl.value());
3420 SkASSERT(!var.modifierFlags().isConst());
3421 visitor->visitNonconstantVariable(var);
3422 }
3423 }
3424}
3425
3427 class : public ThreadgroupStructVisitor {
3428 public:
3429 void visitNonconstantVariable(const Variable& var) override {
3430 this->addElement();
3431 fCodeGen->write(" ");
3432 fCodeGen->writeModifiers(var.modifierFlags());
3433 fCodeGen->writeType(var.type());
3434 fCodeGen->write(" ");
3435 fCodeGen->writeName(var.mangledName());
3436 fCodeGen->write(";\n");
3437 }
3438 void addElement() {
3439 if (fFirst) {
3440 fCodeGen->write("struct Threadgroups {\n");
3441 fFirst = false;
3442 }
3443 }
3444 void finish() {
3445 if (!fFirst) {
3446 fCodeGen->writeLine("};");
3447 fFirst = true;
3448 }
3449 }
3450
3451 MetalCodeGenerator* fCodeGen = nullptr;
3452 bool fFirst = true;
3453 } visitor;
3454
3455 visitor.fCodeGen = this;
3456 this->visitThreadgroupStruct(&visitor);
3457 visitor.finish();
3458}
3459
3461 class : public ThreadgroupStructVisitor {
3462 public:
3463 void visitNonconstantVariable(const Variable& var) override {
3464 this->addElement();
3465 fCodeGen->write("{}");
3466 }
3467 void addElement() {
3468 if (fFirst) {
3469 fCodeGen->write("threadgroup Threadgroups _threadgroups{");
3470 fFirst = false;
3471 } else {
3472 fCodeGen->write(", ");
3473 }
3474 }
3475 void finish() {
3476 if (!fFirst) {
3477 fCodeGen->writeLine("};");
3478 fCodeGen->writeLine("(void)_threadgroups;");
3479 }
3480 }
3481 MetalCodeGenerator* fCodeGen = nullptr;
3482 bool fFirst = true;
3483 } visitor;
3484
3485 visitor.fCodeGen = this;
3486 this->visitThreadgroupStruct(&visitor);
3487 visitor.finish();
3488}
3489
3491 switch (e.kind()) {
3493 break;
3494 case ProgramElement::Kind::kGlobalVar:
3495 break;
3496 case ProgramElement::Kind::kInterfaceBlock:
3497 // Handled in writeInterfaceBlocks; do nothing.
3498 break;
3499 case ProgramElement::Kind::kStructDefinition:
3500 // Handled in writeStructDefinitions; do nothing.
3501 break;
3504 break;
3505 case ProgramElement::Kind::kFunctionPrototype:
3507 break;
3508 case ProgramElement::Kind::kModifiers:
3509 // Not necessary in Metal; do nothing.
3510 break;
3511 default:
3512 SkDEBUGFAILF("unsupported program element: %s\n", e.description().c_str());
3513 break;
3514 }
3515}
3516
3518 class RequirementsVisitor : public ProgramVisitor {
3519 public:
3521
3522 bool visitExpression(const Expression& e) override {
3523 switch (e.kind()) {
3524 case Expression::Kind::kFunctionCall: {
3525 const FunctionCall& f = e.as<FunctionCall>();
3526 fRequirements |= fCodeGen->requirements(f.function());
3527 break;
3528 }
3529 case Expression::Kind::kFieldAccess: {
3530 const FieldAccess& f = e.as<FieldAccess>();
3531 if (f.ownerKind() == FieldAccess::OwnerKind::kAnonymousInterfaceBlock) {
3533 return false; // don't recurse into the base variable
3534 }
3535 break;
3536 }
3537 case Expression::Kind::kVariableReference: {
3538 const Variable& var = *e.as<VariableReference>().variable();
3539
3540 if (var.layout().fBuiltin == SK_FRAGCOORD_BUILTIN) {
3542 } else if (var.layout().fBuiltin == SK_SAMPLEMASKIN_BUILTIN) {
3544 } else if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3546 } else if (var.layout().fBuiltin == SK_VERTEXID_BUILTIN) {
3548 } else if (var.layout().fBuiltin == SK_INSTANCEID_BUILTIN) {
3550 } else if (var.storage() == Variable::Storage::kGlobal) {
3551 if (is_input(var)) {
3553 } else if (is_output(var)) {
3555 } else if (is_uniforms(var)) {
3557 } else if (is_threadgroup(var)) {
3559 } else if (is_in_globals(var)) {
3561 }
3562 }
3563 break;
3564 }
3565 default:
3566 break;
3567 }
3568 return INHERITED::visitExpression(e);
3569 }
3570
3571 MetalCodeGenerator* fCodeGen;
3573 using INHERITED = ProgramVisitor;
3574 };
3575
3576 RequirementsVisitor visitor;
3577 if (s) {
3578 visitor.fCodeGen = this;
3579 visitor.visitStatement(*s);
3580 }
3581 return visitor.fRequirements;
3582}
3583
3585 Requirements* found = fRequirements.find(&f);
3586 if (!found) {
3588 for (const ProgramElement* e : fProgram.elements()) {
3589 if (e->is<FunctionDefinition>()) {
3590 const FunctionDefinition& def = e->as<FunctionDefinition>();
3591 if (&def.declaration() == &f) {
3592 Requirements reqs = this->requirements(def.body().get());
3593 fRequirements.set(&f, reqs);
3594 return reqs;
3595 }
3596 }
3597 }
3598
3599 // Loading data from the instrinsic float buffer requires access to the Globals struct.
3600 if (f.intrinsicKind() == IntrinsicKind::k_loadFloatBuffer_IntrinsicKind) {
3602 fRequirements.set(&f, reqs);
3603 return reqs;
3604 }
3605
3606 // We never found a definition for this declared function, but it's legal to prototype a
3607 // function without ever giving a definition, as long as you don't call it.
3608 return kNo_Requirements;
3609 }
3610 return *found;
3611}
3612
3615 {
3616 AutoOutputStream outputToHeader(this, &header, &fIndentation);
3617 this->writeHeader();
3618 this->writeConstantVariables();
3619 this->writeSampler2DPolyfill();
3620 this->writeStructDefinitions();
3621 this->writeUniformStruct();
3622 this->writeInputStruct();
3624 this->writeOutputStruct();
3625 }
3626 this->writeInterfaceBlocks();
3627 this->writeGlobalStruct();
3628 this->writeThreadgroupStruct();
3629
3630 // Emit prototypes for every built-in function; these aren't always added in perfect order.
3631 for (const ProgramElement* e : fProgram.fSharedElements) {
3632 if (e->is<FunctionDefinition>()) {
3634 this->writeLine(";");
3635 }
3636 }
3637 }
3638 StringStream body;
3639 {
3640 AutoOutputStream outputToBody(this, &body, &fIndentation);
3641
3642 for (const ProgramElement* e : fProgram.elements()) {
3643 this->writeProgramElement(*e);
3644 }
3645 }
3646 write_stringstream(header, *fOut);
3649 write_stringstream(body, *fOut);
3650 return fContext.fErrors->errorCount() == 0;
3651}
3652
3653bool ToMetal(Program& program, const ShaderCaps* caps, OutputStream& out) {
3654 TRACE_EVENT0("skia.shaders", "SkSL::ToMetal");
3655 SkASSERT(caps != nullptr);
3656
3657 program.fContext->fErrors->setSource(*program.fSource);
3658 MetalCodeGenerator cg(program.fContext.get(), caps, &program, &out);
3659 bool result = cg.generateCode();
3660 program.fContext->fErrors->setSource(std::string_view());
3661
3662 return result;
3663}
3664
3665bool ToMetal(Program& program, const ShaderCaps* caps, std::string* out) {
3667 if (!ToMetal(program, caps, buffer)) {
3668 return false;
3669 }
3670 *out = buffer.str();
3671 return true;
3672}
3673
3674} // namespace SkSL
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkPoint pos
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkDEBUGFAILF(fmt,...)
Definition: SkAssert.h:119
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkASSERTF(cond, fmt,...)
Definition: SkAssert.h:117
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
constexpr int SK_SAMPLEMASK_BUILTIN
Definition: SkSLCompiler.h:33
constexpr int SK_WORKGROUPID_BUILTIN
Definition: SkSLCompiler.h:41
constexpr int SK_CLOCKWISE_BUILTIN
Definition: SkSLCompiler.h:31
constexpr int SK_VERTEXID_BUILTIN
Definition: SkSLCompiler.h:35
constexpr int SK_FRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:27
constexpr int SK_LASTFRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:28
constexpr int SK_GLOBALINVOCATIONID_BUILTIN
Definition: SkSLCompiler.h:43
constexpr int SK_POSITION_BUILTIN
Definition: SkSLCompiler.h:37
constexpr int SK_LOCALINVOCATIONID_BUILTIN
Definition: SkSLCompiler.h:42
constexpr int SK_INSTANCEID_BUILTIN
Definition: SkSLCompiler.h:36
constexpr int SK_SECONDARYFRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:29
constexpr int SK_NUMWORKGROUPS_BUILTIN
Definition: SkSLCompiler.h:40
constexpr int SK_POINTSIZE_BUILTIN
Definition: SkSLCompiler.h:38
constexpr int SK_FRAGCOORD_BUILTIN
Definition: SkSLCompiler.h:30
constexpr int SK_LOCALINVOCATIONINDEX_BUILTIN
Definition: SkSLCompiler.h:44
constexpr int SK_SAMPLEMASKIN_BUILTIN
Definition: SkSLCompiler.h:32
#define SKSL_RTFLIP_NAME
Definition: SkSLProgram.h:19
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
GLenum type
virtual SkSpan< std::unique_ptr< Expression > > argumentSpan()=0
const StatementArray & children() const
Definition: SkSLBlock.h:71
const std::unique_ptr< Type > fHalf4
const std::unique_ptr< Type > fUShort
const std::unique_ptr< Type > fAtomicUInt
const std::unique_ptr< Type > fUInt
static constexpr float kSharpenTexturesBias
const ShaderCaps & fCaps
const Program & fProgram
CodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *stream)
const BuiltinTypes & fTypes
Definition: SkSLContext.h:30
ErrorReporter * fErrors
Definition: SkSLContext.h:36
std::unique_ptr< Expression > & test()
std::unique_ptr< Statement > & statement()
void error(Position position, std::string_view msg)
ExpressionArray clone() const
const std::unique_ptr< Expression > & expression() const
Kind kind() const
const Type & type() const
std::string description() const final
AnyConstructor & asAnyConstructor()
std::unique_ptr< Expression > & base()
std::unique_ptr< Statement > & statement()
std::unique_ptr< Expression > & next()
std::unique_ptr< Expression > & test()
std::unique_ptr< Statement > & initializer()
ExpressionArray & arguments()
const FunctionDeclaration & function() const
std::unique_ptr< Statement > & body()
const FunctionDeclaration & declaration() const
const FunctionDeclaration & declaration() const
VarDeclaration & varDeclaration()
Position position() const
Definition: SkSLIRNode.h:111
bool is() const
Definition: SkSLIRNode.h:124
const T & as() const
Definition: SkSLIRNode.h:133
virtual std::string description() const =0
Position fPosition
Definition: SkSLIRNode.h:109
std::unique_ptr< Expression > & test()
std::unique_ptr< Statement > & ifTrue()
std::unique_ptr< Statement > & ifFalse()
std::unique_ptr< Expression > & base()
std::unique_ptr< Expression > & index()
std::string_view instanceName() const
Variable * var() const
std::string_view typeName() const
std::string description(OperatorPrecedence) const override
Definition: SkSLLiteral.cpp:13
SKSL_INT intValue() const
Definition: SkSLLiteral.h:97
size_t size(const Type &type) const
size_t isSupported(const Type &type) const
size_t alignment(const Type &type) const
virtual void visitTexture(const Type &type, std::string_view name)
virtual void visitConstantVariable(const VarDeclaration &decl)
virtual void visitSampler(const Type &type, std::string_view name)
virtual void visitInterfaceBlock(const InterfaceBlock &block, std::string_view blockName)
virtual void visitNonconstantVariable(const Variable &var, const Expression *value)
virtual void visitNonconstantVariable(const Variable &var)=0
void assembleMatrixFromExpressions(const AnyConstructor &ctor, int columns, int rows)
static constexpr Requirements kFragCoord_Requirement
void writeConstructorMatrixResize(const ConstructorMatrixResize &c, Precedence parentPrecedence)
void writeSwizzle(const Swizzle &swizzle)
skia_private::THashMap< const FunctionDeclaration *, Requirements > fRequirements
void writeNumberAsMatrix(const Expression &expr, const Type &matrixType)
void writeConstructorCompoundVector(const ConstructorCompound &c, Precedence parentPrecedence)
void writeExpression(const Expression &expr, Precedence parentPrecedence)
void writePrefixExpression(const PrefixExpression &p, Precedence parentPrecedence)
std::string splatMatrixOf1(const Type &type)
void writeIndexExpression(const IndexExpression &expr)
void writeStructDefinition(const StructDefinition &s)
void writeCastConstructor(const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)
std::string getVectorFromMat2x2ConstructorHelper(const Type &matrixType)
void writeArgumentList(const ExpressionArray &arguments)
void writeType(const Type &type)
std::string typeName(const Type &type)
static constexpr char kSamplerSuffix[]
bool writeFunctionDeclaration(const FunctionDeclaration &f)
skia_private::THashSet< std::string > fHelpers
void writeExpressionStatement(const ExpressionStatement &s)
void writeFunction(const FunctionDefinition &f)
void writeSwitchStatement(const SwitchStatement &s)
void visitGlobalStruct(GlobalStructVisitor *visitor)
void writeIndexInnerExpression(const Expression &expr)
int size(const Type *type, bool isPacked) const
std::string getInversePolyfill(const ExpressionArray &arguments)
void write(std::string_view s)
void writeTernaryExpression(const TernaryExpression &t, Precedence parentPrecedence)
void writeReturnStatement(const ReturnStatement &r)
void writeIfStatement(const IfStatement &stmt)
void writeVariableReference(const VariableReference &ref)
void writeFunctionCall(const FunctionCall &c)
static constexpr Requirements kGlobals_Requirement
int getUniformSet(const Layout &layout)
void visitThreadgroupStruct(ThreadgroupStructVisitor *visitor)
void writeMatrixDivisionHelpers(const Type &type)
bool matrixConstructHelperIsNeeded(const ConstructorCompound &c)
void writeConstructorArrayCast(const ConstructorArrayCast &c, Precedence parentPrecedence)
void writeArrayEqualityHelpers(const Type &type)
static constexpr Requirements kVertexID_Requirement
static constexpr char kTextureSuffix[]
void writeFields(SkSpan< const Field > fields, Position pos)
static constexpr Requirements kInputs_Requirement
static constexpr Requirements kOutputs_Requirement
void writeMatrixEqualityHelpers(const Type &left, const Type &right)
void writeLiteral(const Literal &f)
void writeLine(std::string_view s=std::string_view())
void writeBinaryExpressionElement(const Expression &expr, Operator op, const Expression &other, Precedence precedence)
int alignment(const Type *type, bool isPacked) const
void writePostfixExpression(const PostfixExpression &p, Precedence parentPrecedence)
void writeMinAbsHack(Expression &absExpr, Expression &otherExpr)
void writeWithIndexSubstitution(const std::function< void()> &fn)
void writeName(std::string_view name)
void writeLayout(const Layout &layout)
void writeStatements(const StatementArray &statements)
void writeDoStatement(const DoStatement &d)
static constexpr Requirements kSampleMaskIn_Requirement
std::unique_ptr< IndexSubstitutionData > fIndexSubstitutionData
void writeBinaryExpression(const BinaryExpression &b, Precedence parentPrecedence)
static constexpr Requirements kNo_Requirements
void writeStatement(const Statement &s)
skia_private::THashMap< const Type *, std::string > fInterfaceBlockNameMap
bool writeIntrinsicCall(const FunctionCall &c, IntrinsicKind kind)
void writeStructEqualityHelpers(const Type &type)
static constexpr Requirements kUniforms_Requirement
void writeVarDeclaration(const VarDeclaration &decl)
static constexpr Requirements kInstanceID_Requirement
void writeConstructorCompoundMatrix(const ConstructorCompound &c, Precedence parentPrecedence)
Requirements requirements(const FunctionDeclaration &f)
void writeSimpleIntrinsic(const FunctionCall &c)
void writeEqualityHelpers(const Type &leftType, const Type &rightType)
void writeExtension(const Extension &ext)
const FunctionDeclaration * fCurrentFunction
void writeProgramElement(const ProgramElement &e)
void writeFunctionPrototype(const FunctionPrototype &f)
void writeFieldAccess(const FieldAccess &f)
std::string getBitcastIntrinsic(const Type &outType)
void writeConstructorCompound(const ConstructorCompound &c, Precedence parentPrecedence)
skia_private::THashSet< std::string_view > fReservedWords
void writeForStatement(const ForStatement &f)
std::string getTempVariable(const Type &varType)
void writeFunctionRequirementParams(const FunctionDeclaration &f, const char *&separator)
static constexpr Requirements kThreadgroups_Requirement
void writeInterfaceBlock(const InterfaceBlock &intf)
MetalCodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *out)
void writeAnyConstructor(const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)
void writeFunctionRequirementArgs(const FunctionDeclaration &f, const char *&separator)
void writeMatrixTimesEqualHelper(const Type &left, const Type &right, const Type &result)
void writeVarInitializer(const Variable &var, const Expression &value)
void assembleMatrixFromMatrix(const Type &sourceMatrix, int columns, int rows)
void writeModifiers(ModifierFlags flags)
std::string getMatrixConstructHelper(const AnyConstructor &c)
int getUniformBinding(const Layout &layout)
bool isWorkgroup() const
ExpressionArray & arguments()
SkSpan< std::unique_ptr< Expression > > argumentSpan() final
Kind kind() const
Definition: SkSLOperator.h:85
std::string_view tightOperatorName() const
OperatorPrecedence getBinaryPrecedence() const
const char * operatorName() const
Operator removeAssignment() const
bool isCompoundAssignment() const
bool isValidForMatrixOrVector() const
bool isEquality() const
Definition: SkSLOperator.h:87
void printf(const char format[],...) SK_PRINTF_LIKE(2
virtual void writeText(const char *s)=0
std::unique_ptr< Expression > & operand()
std::unique_ptr< Expression > & operand()
std::unique_ptr< Expression > & expression()
std::unique_ptr< Expression > toLiteral(const ShaderCaps &caps) const
Definition: SkSLSetting.cpp:88
std::unique_ptr< Expression > & argument()
void write8(uint8_t b) override
void writeText(const char *s) override
const std::string & str() const
SKSL_INT value() const
bool isDefault() const
std::unique_ptr< Statement > & statement()
std::unique_ptr< Expression > & value()
std::unique_ptr< Expression > & base()
Definition: SkSLSwizzle.h:82
const ComponentArray & components() const
Definition: SkSLSwizzle.h:90
static std::string MaskString(const ComponentArray &inComponents)
std::string_view name() const
Definition: SkSLSymbol.h:51
const Type & type() const
Definition: SkSLSymbol.h:42
virtual bool visitStatement(typename T::Statement &statement)
std::unique_ptr< Expression > & ifTrue()
std::unique_ptr< Expression > & test()
std::unique_ptr< Expression > & ifFalse()
virtual bool isArray() const
Definition: SkSLType.h:532
bool highPrecision() const
Definition: SkSLType.h:570
virtual bool isVector() const
Definition: SkSLType.h:524
virtual int rows() const
Definition: SkSLType.h:438
virtual const Type & componentType() const
Definition: SkSLType.h:404
bool isNumber() const
Definition: SkSLType.h:304
virtual SkSpan< const Field > fields() const
Definition: SkSLType.h:469
bool matches(const Type &other) const
Definition: SkSLType.h:269
virtual bool isMatrix() const
Definition: SkSLType.h:528
virtual const Type & resolve() const
Definition: SkSLType.h:264
virtual const Type & textureType() const
Definition: SkSLType.h:419
virtual int columns() const
Definition: SkSLType.h:429
virtual bool isScalar() const
Definition: SkSLType.h:512
virtual const Type & scalarTypeForLiteral() const
Definition: SkSLType.h:520
virtual bool isUnsizedArray() const
Definition: SkSLType.h:536
const Type & columnType(const Context &context) const
Definition: SkSLType.h:411
std::string displayName() const
Definition: SkSLType.h:234
TypeKind typeKind() const
Definition: SkSLType.h:283
virtual bool isStruct() const
Definition: SkSLType.h:540
virtual SpvDim_ dimensions() const
Definition: SkSLType.h:481
bool isInteger() const
Definition: SkSLType.h:339
std::unique_ptr< Expression > & value()
Variable * var() const
const Variable * variable() const
virtual std::string_view mangledName() const
Definition: SkSLVariable.h:132
Storage storage() const
Definition: SkSLVariable.h:103
ModifierFlags modifierFlags() const
Definition: SkSLVariable.h:89
virtual const Layout & layout() const
constexpr size_t size() const
Definition: SkSpan_impl.h:95
T * push_back_n(int n)
Definition: SkTArray.h:267
int size() const
Definition: SkTArray.h:421
void add(T item)
Definition: SkTHash.h:592
bool contains(const T &item) const
Definition: SkTHash.h:595
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct s
EMSCRIPTEN_KEEPALIVE void empty()
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
static float max(float r, float g, float b)
Definition: hsl.cpp:49
bool IsTrivialExpression(const Expression &expr)
bool HasSideEffects(const Expression &expr)
bool IsAssignable(Expression &expr, AssignmentInfo *info=nullptr, ErrorReporter *errors=nullptr)
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
std::string void void auto Separator()
Definition: SkSLString.h:30
std::string void appendf(std::string *str, const char *fmt,...) SK_PRINTF_LIKE(2
Definition: SkSLString.cpp:92
static bool is_uniforms(const Variable &var)
static constexpr char kInverse3x3[]
static bool is_buffer(const InterfaceBlock &block)
static constexpr char kInverse2x2[]
void write_stringstream(const StringStream &s, OutputStream &out)
Definition: SkSLUtil.cpp:42
static bool is_in_globals(const Variable &var)
static constexpr char kInverse4x4[]
static bool is_input(const Variable &var)
static bool is_readonly(const InterfaceBlock &block)
static bool is_block_ending_with_return(const Statement *stmt)
static bool is_threadgroup(const Variable &var)
bool ToMetal(Program &program, const ShaderCaps *caps, OutputStream &out)
static bool pass_by_reference(const Type &type, ModifierFlags flags)
OperatorPrecedence
Definition: SkSLOperator.h:57
static const char * operator_name(Operator op)
static bool is_output(const Variable &var)
static bool is_compute_builtin(const Variable &var)
static bool needs_address_space(const Type &type, ModifierFlags modifiers)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
static SkString to_string(int n)
Definition: nanobench.cpp:119
static const char header[]
Definition: skpbench.cpp:88
@ SpvDim2D
Definition: spirv.h:143
Layout fLayout
Definition: SkSLType.h:85
std::string_view fName
Definition: SkSLType.h:87
static std::unique_ptr< Expression > LoadFloatBuffer(const Context &context, const SkSL::ShaderCaps &shaderCaps, Position position, std::unique_ptr< Expression > idx)
static bool IsVertex(ProgramKind kind)
static bool IsFragment(ProgramKind kind)
static bool IsCompute(ProgramKind kind)
ElementsCollection elements() const
Definition: SkSLProgram.h:140
std::shared_ptr< Context > fContext
Definition: SkSLProgram.h:154
ProgramInterface fInterface
Definition: SkSLProgram.h:165
std::vector< const ProgramElement * > fSharedElements
Definition: SkSLProgram.h:164
std::unique_ptr< std::string > fSource
Definition: SkSLProgram.h:152
std::unique_ptr< ProgramConfig > fConfig
Definition: SkSLProgram.h:153
bool fDualSourceBlendingSupport
Definition: SkSLUtil.h:84
const char * fFBFetchColorName
Definition: SkSLUtil.h:158
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131