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