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