xref: /aosp_15_r20/external/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 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 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLWGSLCodeGenerator.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkStringView.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLIntrinsicList.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLMemoryLayout.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOutputStream.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLString.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLStringStream.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLUtil.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramUsage.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramVisitor.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLCodeGenTypes.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLCodeGenerator.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBlock.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructor.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorArrayCast.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompound.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLDoStatement.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpressionStatement.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldAccess.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldSymbol.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLForStatement.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionCall.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRHelpers.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIfStatement.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIndexExpression.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLInterfaceBlock.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifiersDeclaration.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPostfixExpression.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLReturnStatement.h"
68*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSetting.h"
69*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
70*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStructDefinition.h"
71*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchCase.h"
72*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchStatement.h"
73*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
74*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
75*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
76*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
77*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
78*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/spirv.h"
80*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
81*c8dee2aaSAndroid Build Coastguard Worker 
82*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
83*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
84*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
85*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
86*c8dee2aaSAndroid Build Coastguard Worker #include <iterator>
87*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
88*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
89*c8dee2aaSAndroid Build Coastguard Worker #include <string>
90*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
91*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker namespace {
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker // Represents a function's dependencies that are not accessible in global scope. For instance,
98*c8dee2aaSAndroid Build Coastguard Worker // pipeline stage input and output parameters must be passed in as an argument.
99*c8dee2aaSAndroid Build Coastguard Worker //
100*c8dee2aaSAndroid Build Coastguard Worker // This is a bitmask enum. (It would be inside `class WGSLCodeGenerator`, but this leads to build
101*c8dee2aaSAndroid Build Coastguard Worker // errors in MSVC.)
102*c8dee2aaSAndroid Build Coastguard Worker enum class WGSLFunctionDependency : uint8_t {
103*c8dee2aaSAndroid Build Coastguard Worker     kNone = 0,
104*c8dee2aaSAndroid Build Coastguard Worker     kPipelineInputs  = 1 << 0,
105*c8dee2aaSAndroid Build Coastguard Worker     kPipelineOutputs = 1 << 1,
106*c8dee2aaSAndroid Build Coastguard Worker };
107*c8dee2aaSAndroid Build Coastguard Worker using WGSLFunctionDependencies = SkEnumBitMask<WGSLFunctionDependency>;
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker SK_MAKE_BITMASK_OPS(WGSLFunctionDependency)
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker class WGSLCodeGenerator : public CodeGenerator {
116*c8dee2aaSAndroid Build Coastguard Worker public:
117*c8dee2aaSAndroid Build Coastguard Worker     // See https://www.w3.org/TR/WGSL/#builtin-values
118*c8dee2aaSAndroid Build Coastguard Worker     enum class Builtin {
119*c8dee2aaSAndroid Build Coastguard Worker         // Vertex stage:
120*c8dee2aaSAndroid Build Coastguard Worker         kVertexIndex,    // input
121*c8dee2aaSAndroid Build Coastguard Worker         kInstanceIndex,  // input
122*c8dee2aaSAndroid Build Coastguard Worker         kPosition,       // output, fragment stage input
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker         // Fragment stage:
125*c8dee2aaSAndroid Build Coastguard Worker         kLastFragColor,  // input
126*c8dee2aaSAndroid Build Coastguard Worker         kFrontFacing,    // input
127*c8dee2aaSAndroid Build Coastguard Worker         kSampleIndex,    // input
128*c8dee2aaSAndroid Build Coastguard Worker         kFragDepth,      // output
129*c8dee2aaSAndroid Build Coastguard Worker         kSampleMaskIn,   // input
130*c8dee2aaSAndroid Build Coastguard Worker         kSampleMask,     // output
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker         // Compute stage:
133*c8dee2aaSAndroid Build Coastguard Worker         kLocalInvocationId,     // input
134*c8dee2aaSAndroid Build Coastguard Worker         kLocalInvocationIndex,  // input
135*c8dee2aaSAndroid Build Coastguard Worker         kGlobalInvocationId,    // input
136*c8dee2aaSAndroid Build Coastguard Worker         kWorkgroupId,           // input
137*c8dee2aaSAndroid Build Coastguard Worker         kNumWorkgroups,         // input
138*c8dee2aaSAndroid Build Coastguard Worker     };
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     // Variable declarations can be terminated by:
141*c8dee2aaSAndroid Build Coastguard Worker     //   - comma (","), e.g. in struct member declarations or function parameters
142*c8dee2aaSAndroid Build Coastguard Worker     //   - semicolon (";"), e.g. in function scope variables
143*c8dee2aaSAndroid Build Coastguard Worker     // A "none" option is provided to skip the delimiter when not needed, e.g. at the end of a list
144*c8dee2aaSAndroid Build Coastguard Worker     // of declarations.
145*c8dee2aaSAndroid Build Coastguard Worker     enum class Delimiter {
146*c8dee2aaSAndroid Build Coastguard Worker         kComma,
147*c8dee2aaSAndroid Build Coastguard Worker         kSemicolon,
148*c8dee2aaSAndroid Build Coastguard Worker         kNone,
149*c8dee2aaSAndroid Build Coastguard Worker     };
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     struct ProgramRequirements {
152*c8dee2aaSAndroid Build Coastguard Worker         using DepsMap = skia_private::THashMap<const FunctionDeclaration*,
153*c8dee2aaSAndroid Build Coastguard Worker                                                WGSLFunctionDependencies>;
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker         // Mappings used to synthesize function parameters according to dependencies on pipeline
156*c8dee2aaSAndroid Build Coastguard Worker         // input/output variables.
157*c8dee2aaSAndroid Build Coastguard Worker         DepsMap fDependencies;
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker         // These flags track extensions that will need to be enabled.
160*c8dee2aaSAndroid Build Coastguard Worker         bool fPixelLocalExtension = false;
161*c8dee2aaSAndroid Build Coastguard Worker     };
162*c8dee2aaSAndroid Build Coastguard Worker 
WGSLCodeGenerator(const Context * context,const ShaderCaps * caps,const Program * program,OutputStream * out,PrettyPrint pp,IncludeSyntheticCode isc)163*c8dee2aaSAndroid Build Coastguard Worker     WGSLCodeGenerator(const Context* context,
164*c8dee2aaSAndroid Build Coastguard Worker                       const ShaderCaps* caps,
165*c8dee2aaSAndroid Build Coastguard Worker                       const Program* program,
166*c8dee2aaSAndroid Build Coastguard Worker                       OutputStream* out,
167*c8dee2aaSAndroid Build Coastguard Worker                       PrettyPrint pp,
168*c8dee2aaSAndroid Build Coastguard Worker                       IncludeSyntheticCode isc)
169*c8dee2aaSAndroid Build Coastguard Worker             : CodeGenerator(context, caps, program, out)
170*c8dee2aaSAndroid Build Coastguard Worker             , fPrettyPrint(pp)
171*c8dee2aaSAndroid Build Coastguard Worker             , fGenSyntheticCode(isc) {}
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker     bool generateCode() override;
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker private:
176*c8dee2aaSAndroid Build Coastguard Worker     using Precedence = OperatorPrecedence;
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     // Called by generateCode() as the first step.
179*c8dee2aaSAndroid Build Coastguard Worker     void preprocessProgram();
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker     // Write output content while correctly handling indentation.
182*c8dee2aaSAndroid Build Coastguard Worker     void write(std::string_view s);
183*c8dee2aaSAndroid Build Coastguard Worker     void writeLine(std::string_view s = std::string_view());
184*c8dee2aaSAndroid Build Coastguard Worker     void finishLine();
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker     // Helpers to declare a pipeline stage IO parameter declaration.
187*c8dee2aaSAndroid Build Coastguard Worker     void writePipelineIODeclaration(const Layout& layout,
188*c8dee2aaSAndroid Build Coastguard Worker                                     const Type& type,
189*c8dee2aaSAndroid Build Coastguard Worker                                     ModifierFlags modifiers,
190*c8dee2aaSAndroid Build Coastguard Worker                                     std::string_view name,
191*c8dee2aaSAndroid Build Coastguard Worker                                     Delimiter delimiter);
192*c8dee2aaSAndroid Build Coastguard Worker     void writeUserDefinedIODecl(const Layout& layout,
193*c8dee2aaSAndroid Build Coastguard Worker                                 const Type& type,
194*c8dee2aaSAndroid Build Coastguard Worker                                 ModifierFlags modifiers,
195*c8dee2aaSAndroid Build Coastguard Worker                                 std::string_view name,
196*c8dee2aaSAndroid Build Coastguard Worker                                 Delimiter delimiter);
197*c8dee2aaSAndroid Build Coastguard Worker     void writeBuiltinIODecl(const Type& type,
198*c8dee2aaSAndroid Build Coastguard Worker                             std::string_view name,
199*c8dee2aaSAndroid Build Coastguard Worker                             Builtin builtin,
200*c8dee2aaSAndroid Build Coastguard Worker                             Delimiter delimiter);
201*c8dee2aaSAndroid Build Coastguard Worker     void writeVariableDecl(const Layout& layout,
202*c8dee2aaSAndroid Build Coastguard Worker                            const Type& type,
203*c8dee2aaSAndroid Build Coastguard Worker                            std::string_view name,
204*c8dee2aaSAndroid Build Coastguard Worker                            Delimiter delimiter);
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker     // Write a function definition.
207*c8dee2aaSAndroid Build Coastguard Worker     void writeFunction(const FunctionDefinition& f);
208*c8dee2aaSAndroid Build Coastguard Worker     void writeFunctionDeclaration(const FunctionDeclaration& f,
209*c8dee2aaSAndroid Build Coastguard Worker                                   SkSpan<const bool> paramNeedsDedicatedStorage);
210*c8dee2aaSAndroid Build Coastguard Worker 
211*c8dee2aaSAndroid Build Coastguard Worker     // Write the program entry point.
212*c8dee2aaSAndroid Build Coastguard Worker     void writeEntryPoint(const FunctionDefinition& f);
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker     // Writers for supported statement types.
215*c8dee2aaSAndroid Build Coastguard Worker     void writeStatement(const Statement& s);
216*c8dee2aaSAndroid Build Coastguard Worker     void writeStatements(const StatementArray& statements);
217*c8dee2aaSAndroid Build Coastguard Worker     void writeBlock(const Block& b);
218*c8dee2aaSAndroid Build Coastguard Worker     void writeDoStatement(const DoStatement& expr);
219*c8dee2aaSAndroid Build Coastguard Worker     void writeExpressionStatement(const Expression& expr);
220*c8dee2aaSAndroid Build Coastguard Worker     void writeForStatement(const ForStatement& s);
221*c8dee2aaSAndroid Build Coastguard Worker     void writeIfStatement(const IfStatement& s);
222*c8dee2aaSAndroid Build Coastguard Worker     void writeReturnStatement(const ReturnStatement& s);
223*c8dee2aaSAndroid Build Coastguard Worker     void writeSwitchStatement(const SwitchStatement& s);
224*c8dee2aaSAndroid Build Coastguard Worker     void writeSwitchCases(SkSpan<const SwitchCase* const> cases);
225*c8dee2aaSAndroid Build Coastguard Worker     void writeEmulatedSwitchFallthroughCases(SkSpan<const SwitchCase* const> cases,
226*c8dee2aaSAndroid Build Coastguard Worker                                              std::string_view switchValue);
227*c8dee2aaSAndroid Build Coastguard Worker     void writeSwitchCaseList(SkSpan<const SwitchCase* const> cases);
228*c8dee2aaSAndroid Build Coastguard Worker     void writeVarDeclaration(const VarDeclaration& varDecl);
229*c8dee2aaSAndroid Build Coastguard Worker 
230*c8dee2aaSAndroid Build Coastguard Worker     // Synthesizes an LValue for an expression.
231*c8dee2aaSAndroid Build Coastguard Worker     class LValue;
232*c8dee2aaSAndroid Build Coastguard Worker     class PointerLValue;
233*c8dee2aaSAndroid Build Coastguard Worker     class SwizzleLValue;
234*c8dee2aaSAndroid Build Coastguard Worker     class VectorComponentLValue;
235*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> makeLValue(const Expression& e);
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker     std::string variableReferenceNameForLValue(const VariableReference& r);
238*c8dee2aaSAndroid Build Coastguard Worker     std::string variablePrefix(const Variable& v);
239*c8dee2aaSAndroid Build Coastguard Worker 
240*c8dee2aaSAndroid Build Coastguard Worker     bool binaryOpNeedsComponentwiseMatrixPolyfill(const Type& left, const Type& right, Operator op);
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     // Writers for expressions. These return the final expression text as a string, and emit any
243*c8dee2aaSAndroid Build Coastguard Worker     // necessary setup code directly into the program as necessary. The returned expression may be
244*c8dee2aaSAndroid Build Coastguard Worker     // a `let`-alias that cannot be assigned-into; use `makeLValue` for an assignable expression.
245*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleExpression(const Expression& e, Precedence parentPrecedence);
246*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence);
247*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleBinaryExpression(const Expression& left,
248*c8dee2aaSAndroid Build Coastguard Worker                                          Operator op,
249*c8dee2aaSAndroid Build Coastguard Worker                                          const Expression& right,
250*c8dee2aaSAndroid Build Coastguard Worker                                          const Type& resultType,
251*c8dee2aaSAndroid Build Coastguard Worker                                          Precedence parentPrecedence);
252*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleFieldAccess(const FieldAccess& f);
253*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleFunctionCall(const FunctionCall& call, Precedence parentPrecedence);
254*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleIndexExpression(const IndexExpression& i);
255*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleLiteral(const Literal& l);
256*c8dee2aaSAndroid Build Coastguard Worker     std::string assemblePostfixExpression(const PostfixExpression& p, Precedence parentPrecedence);
257*c8dee2aaSAndroid Build Coastguard Worker     std::string assemblePrefixExpression(const PrefixExpression& p, Precedence parentPrecedence);
258*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleSwizzle(const Swizzle& swizzle);
259*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleTernaryExpression(const TernaryExpression& t, Precedence parentPrecedence);
260*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleVariableReference(const VariableReference& r);
261*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleName(std::string_view name);
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleIncrementExpr(const Type& type);
264*c8dee2aaSAndroid Build Coastguard Worker 
265*c8dee2aaSAndroid Build Coastguard Worker     // Intrinsic helper functions.
266*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleIntrinsicCall(const FunctionCall& call,
267*c8dee2aaSAndroid Build Coastguard Worker                                       IntrinsicKind kind,
268*c8dee2aaSAndroid Build Coastguard Worker                                       Precedence parentPrecedence);
269*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleSimpleIntrinsic(std::string_view intrinsicName, const FunctionCall& call);
270*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleUnaryOpIntrinsic(Operator op,
271*c8dee2aaSAndroid Build Coastguard Worker                                          const FunctionCall& call,
272*c8dee2aaSAndroid Build Coastguard Worker                                          Precedence parentPrecedence);
273*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleBinaryOpIntrinsic(Operator op,
274*c8dee2aaSAndroid Build Coastguard Worker                                           const FunctionCall& call,
275*c8dee2aaSAndroid Build Coastguard Worker                                           Precedence parentPrecedence);
276*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleVectorizedIntrinsic(std::string_view intrinsicName,
277*c8dee2aaSAndroid Build Coastguard Worker                                             const FunctionCall& call);
278*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleOutAssignedIntrinsic(std::string_view intrinsicName,
279*c8dee2aaSAndroid Build Coastguard Worker                                              std::string_view returnFieldName,
280*c8dee2aaSAndroid Build Coastguard Worker                                              std::string_view outFieldName,
281*c8dee2aaSAndroid Build Coastguard Worker                                              const FunctionCall& call);
282*c8dee2aaSAndroid Build Coastguard Worker     std::string assemblePartialSampleCall(std::string_view intrinsicName,
283*c8dee2aaSAndroid Build Coastguard Worker                                           const Expression& sampler,
284*c8dee2aaSAndroid Build Coastguard Worker                                           const Expression& coords);
285*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleInversePolyfill(const FunctionCall& call);
286*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleComponentwiseMatrixBinary(const Type& leftType,
287*c8dee2aaSAndroid Build Coastguard Worker                                                   const Type& rightType,
288*c8dee2aaSAndroid Build Coastguard Worker                                                   const std::string& left,
289*c8dee2aaSAndroid Build Coastguard Worker                                                   const std::string& right,
290*c8dee2aaSAndroid Build Coastguard Worker                                                   Operator op);
291*c8dee2aaSAndroid Build Coastguard Worker 
292*c8dee2aaSAndroid Build Coastguard Worker     // Constructor expressions
293*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleAnyConstructor(const AnyConstructor& c);
294*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleConstructorCompound(const ConstructorCompound& c);
295*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleConstructorCompoundVector(const ConstructorCompound& c);
296*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleConstructorCompoundMatrix(const ConstructorCompound& c);
297*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
298*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleConstructorMatrixResize(const ConstructorMatrixResize& ctor);
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker     // Synthesized helper functions for comparison operators that are not supported by WGSL.
301*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleEqualityExpression(const Type& left,
302*c8dee2aaSAndroid Build Coastguard Worker                                            const std::string& leftName,
303*c8dee2aaSAndroid Build Coastguard Worker                                            const Type& right,
304*c8dee2aaSAndroid Build Coastguard Worker                                            const std::string& rightName,
305*c8dee2aaSAndroid Build Coastguard Worker                                            Operator op,
306*c8dee2aaSAndroid Build Coastguard Worker                                            Precedence parentPrecedence);
307*c8dee2aaSAndroid Build Coastguard Worker     std::string assembleEqualityExpression(const Expression& left,
308*c8dee2aaSAndroid Build Coastguard Worker                                            const Expression& right,
309*c8dee2aaSAndroid Build Coastguard Worker                                            Operator op,
310*c8dee2aaSAndroid Build Coastguard Worker                                            Precedence parentPrecedence);
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker     // Writes a scratch variable into the program and returns its name (e.g. `_skTemp123`).
313*c8dee2aaSAndroid Build Coastguard Worker     std::string writeScratchVar(const Type& type, const std::string& value = "");
314*c8dee2aaSAndroid Build Coastguard Worker 
315*c8dee2aaSAndroid Build Coastguard Worker     // Writes a scratch let-variable into the program, gives it the value of `expr`, and returns its
316*c8dee2aaSAndroid Build Coastguard Worker     // name (e.g. `_skTemp123`).
317*c8dee2aaSAndroid Build Coastguard Worker     std::string writeScratchLet(const std::string& expr, bool isCompileTimeConstant = false);
318*c8dee2aaSAndroid Build Coastguard Worker     std::string writeScratchLet(const Expression& expr, Precedence parentPrecedence);
319*c8dee2aaSAndroid Build Coastguard Worker 
320*c8dee2aaSAndroid Build Coastguard Worker     // Converts `expr` into a string and returns a scratch let-variable associated with the
321*c8dee2aaSAndroid Build Coastguard Worker     // expression. Compile-time constants and plain variable references will return the expression
322*c8dee2aaSAndroid Build Coastguard Worker     // directly and omit the let-variable.
323*c8dee2aaSAndroid Build Coastguard Worker     std::string writeNontrivialScratchLet(const Expression& expr, Precedence parentPrecedence);
324*c8dee2aaSAndroid Build Coastguard Worker 
325*c8dee2aaSAndroid Build Coastguard Worker     // Generic recursive ProgramElement visitor.
326*c8dee2aaSAndroid Build Coastguard Worker     void writeProgramElement(const ProgramElement& e);
327*c8dee2aaSAndroid Build Coastguard Worker     void writeGlobalVarDeclaration(const GlobalVarDeclaration& d);
328*c8dee2aaSAndroid Build Coastguard Worker     void writeStructDefinition(const StructDefinition& s);
329*c8dee2aaSAndroid Build Coastguard Worker     void writeModifiersDeclaration(const ModifiersDeclaration&);
330*c8dee2aaSAndroid Build Coastguard Worker 
331*c8dee2aaSAndroid Build Coastguard Worker     // Writes the WGSL struct fields for SkSL structs and interface blocks. Enforces WGSL address
332*c8dee2aaSAndroid Build Coastguard Worker     // space layout constraints
333*c8dee2aaSAndroid Build Coastguard Worker     // (https://www.w3.org/TR/WGSL/#address-space-layout-constraints) if a `layout` is
334*c8dee2aaSAndroid Build Coastguard Worker     // provided. A struct that does not need to be host-shareable does not require a `layout`.
335*c8dee2aaSAndroid Build Coastguard Worker     void writeFields(SkSpan<const Field> fields, const MemoryLayout* memoryLayout = nullptr);
336*c8dee2aaSAndroid Build Coastguard Worker 
337*c8dee2aaSAndroid Build Coastguard Worker     // We bundle uniforms, and all varying pipeline stage inputs and outputs, into separate structs.
338*c8dee2aaSAndroid Build Coastguard Worker     bool needsStageInputStruct() const;
339*c8dee2aaSAndroid Build Coastguard Worker     void writeStageInputStruct();
340*c8dee2aaSAndroid Build Coastguard Worker     bool needsStageOutputStruct() const;
341*c8dee2aaSAndroid Build Coastguard Worker     void writeStageOutputStruct();
342*c8dee2aaSAndroid Build Coastguard Worker     void writeUniformsAndBuffers();
343*c8dee2aaSAndroid Build Coastguard Worker     void prepareUniformPolyfillsForInterfaceBlock(const InterfaceBlock* interfaceBlock,
344*c8dee2aaSAndroid Build Coastguard Worker                                                   std::string_view instanceName,
345*c8dee2aaSAndroid Build Coastguard Worker                                                   MemoryLayout::Standard nativeLayout);
346*c8dee2aaSAndroid Build Coastguard Worker     void writeEnables();
347*c8dee2aaSAndroid Build Coastguard Worker     void writeUniformPolyfills();
348*c8dee2aaSAndroid Build Coastguard Worker 
349*c8dee2aaSAndroid Build Coastguard Worker     void writeTextureOrSampler(const Variable& var,
350*c8dee2aaSAndroid Build Coastguard Worker                                int bindingLocation,
351*c8dee2aaSAndroid Build Coastguard Worker                                std::string_view suffix,
352*c8dee2aaSAndroid Build Coastguard Worker                                std::string_view wgslType);
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker     // Writes all top-level non-opaque global uniform declarations (i.e. not part of an interface
355*c8dee2aaSAndroid Build Coastguard Worker     // block) into a single uniform block binding.
356*c8dee2aaSAndroid Build Coastguard Worker     //
357*c8dee2aaSAndroid Build Coastguard Worker     // In complete fragment/vertex/compute programs, uniforms will be declared only as interface
358*c8dee2aaSAndroid Build Coastguard Worker     // blocks and global opaque types (like textures and samplers) which we expect to be declared
359*c8dee2aaSAndroid Build Coastguard Worker     // with a unique binding and descriptor set index. However, test files that are declared as RTE
360*c8dee2aaSAndroid Build Coastguard Worker     // programs may contain OpenGL-style global uniform declarations with no clear binding index to
361*c8dee2aaSAndroid Build Coastguard Worker     // use for the containing synthesized block.
362*c8dee2aaSAndroid Build Coastguard Worker     //
363*c8dee2aaSAndroid Build Coastguard Worker     // Since we are handling these variables only to generate gold files from RTEs and never run
364*c8dee2aaSAndroid Build Coastguard Worker     // them, we always declare them at the default bind group and binding index.
365*c8dee2aaSAndroid Build Coastguard Worker     void writeNonBlockUniformsForTests();
366*c8dee2aaSAndroid Build Coastguard Worker 
367*c8dee2aaSAndroid Build Coastguard Worker     // For a given function declaration, writes out any implicitly required pipeline stage arguments
368*c8dee2aaSAndroid Build Coastguard Worker     // based on the function's pre-determined dependencies. These are expected to be written out as
369*c8dee2aaSAndroid Build Coastguard Worker     // the first parameters for a function that requires them. Returns true if any arguments were
370*c8dee2aaSAndroid Build Coastguard Worker     // written.
371*c8dee2aaSAndroid Build Coastguard Worker     std::string functionDependencyArgs(const FunctionDeclaration&);
372*c8dee2aaSAndroid Build Coastguard Worker     bool writeFunctionDependencyParams(const FunctionDeclaration&);
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker     // Code in the header appears before the main body of code.
375*c8dee2aaSAndroid Build Coastguard Worker     StringStream fHeader;
376*c8dee2aaSAndroid Build Coastguard Worker 
377*c8dee2aaSAndroid Build Coastguard Worker     // We assign unique names to anonymous interface blocks based on the type.
378*c8dee2aaSAndroid Build Coastguard Worker     skia_private::THashMap<const Type*, std::string> fInterfaceBlockNameMap;
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker     // Stores the functions which use stage inputs/outputs as well as required WGSL extensions.
381*c8dee2aaSAndroid Build Coastguard Worker     ProgramRequirements fRequirements;
382*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<const Variable*> fPipelineInputs;
383*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<const Variable*> fPipelineOutputs;
384*c8dee2aaSAndroid Build Coastguard Worker 
385*c8dee2aaSAndroid Build Coastguard Worker     // These fields track whether we have written the polyfill for `inverse()` for a given matrix
386*c8dee2aaSAndroid Build Coastguard Worker     // type.
387*c8dee2aaSAndroid Build Coastguard Worker     bool fWrittenInverse2 = false;
388*c8dee2aaSAndroid Build Coastguard Worker     bool fWrittenInverse3 = false;
389*c8dee2aaSAndroid Build Coastguard Worker     bool fWrittenInverse4 = false;
390*c8dee2aaSAndroid Build Coastguard Worker     PrettyPrint fPrettyPrint;
391*c8dee2aaSAndroid Build Coastguard Worker     IncludeSyntheticCode fGenSyntheticCode;
392*c8dee2aaSAndroid Build Coastguard Worker 
393*c8dee2aaSAndroid Build Coastguard Worker     // These fields control uniform polyfill support in cases where WGSL and std140 disagree.
394*c8dee2aaSAndroid Build Coastguard Worker     // In std140 layout, matrices need to be represented as arrays of @size(16)-aligned vectors, and
395*c8dee2aaSAndroid Build Coastguard Worker     // array elements are wrapped in a struct containing a single @size(16)-aligned element. Arrays
396*c8dee2aaSAndroid Build Coastguard Worker     // of matrices combine both wrappers. These wrapper structs are unpacked into natively-typed
397*c8dee2aaSAndroid Build Coastguard Worker     // globals at the shader entrypoint.
398*c8dee2aaSAndroid Build Coastguard Worker     struct FieldPolyfillInfo {
399*c8dee2aaSAndroid Build Coastguard Worker         const InterfaceBlock* fInterfaceBlock;
400*c8dee2aaSAndroid Build Coastguard Worker         std::string fReplacementName;
401*c8dee2aaSAndroid Build Coastguard Worker         bool fIsArray = false;
402*c8dee2aaSAndroid Build Coastguard Worker         bool fIsMatrix = false;
403*c8dee2aaSAndroid Build Coastguard Worker         bool fWasAccessed = false;
404*c8dee2aaSAndroid Build Coastguard Worker     };
405*c8dee2aaSAndroid Build Coastguard Worker     using FieldPolyfillMap = skia_private::THashMap<const Field*, FieldPolyfillInfo>;
406*c8dee2aaSAndroid Build Coastguard Worker     FieldPolyfillMap fFieldPolyfillMap;
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker     // Output processing state.
409*c8dee2aaSAndroid Build Coastguard Worker     int fIndentation = 0;
410*c8dee2aaSAndroid Build Coastguard Worker     bool fAtLineStart = false;
411*c8dee2aaSAndroid Build Coastguard Worker     bool fHasUnconditionalReturn = false;
412*c8dee2aaSAndroid Build Coastguard Worker     bool fAtFunctionScope = false;
413*c8dee2aaSAndroid Build Coastguard Worker     int fConditionalScopeDepth = 0;
414*c8dee2aaSAndroid Build Coastguard Worker     int fLocalSizeX = 1;
415*c8dee2aaSAndroid Build Coastguard Worker     int fLocalSizeY = 1;
416*c8dee2aaSAndroid Build Coastguard Worker     int fLocalSizeZ = 1;
417*c8dee2aaSAndroid Build Coastguard Worker 
418*c8dee2aaSAndroid Build Coastguard Worker     int fScratchCount = 0;
419*c8dee2aaSAndroid Build Coastguard Worker };
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker enum class ProgramKind : int8_t;
422*c8dee2aaSAndroid Build Coastguard Worker 
423*c8dee2aaSAndroid Build Coastguard Worker namespace {
424*c8dee2aaSAndroid Build Coastguard Worker 
425*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kSamplerSuffix[] = "_Sampler";
426*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kTextureSuffix[] = "_Texture";
427*c8dee2aaSAndroid Build Coastguard Worker 
428*c8dee2aaSAndroid Build Coastguard Worker // See https://www.w3.org/TR/WGSL/#memory-view-types
429*c8dee2aaSAndroid Build Coastguard Worker enum class PtrAddressSpace {
430*c8dee2aaSAndroid Build Coastguard Worker     kFunction,
431*c8dee2aaSAndroid Build Coastguard Worker     kPrivate,
432*c8dee2aaSAndroid Build Coastguard Worker     kStorage,
433*c8dee2aaSAndroid Build Coastguard Worker };
434*c8dee2aaSAndroid Build Coastguard Worker 
operator_name(Operator op)435*c8dee2aaSAndroid Build Coastguard Worker const char* operator_name(Operator op) {
436*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
437*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LOGICALXOR:  return " != ";
438*c8dee2aaSAndroid Build Coastguard Worker         default:                          return op.operatorName();
439*c8dee2aaSAndroid Build Coastguard Worker     }
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker 
is_reserved_word(std::string_view word)442*c8dee2aaSAndroid Build Coastguard Worker bool is_reserved_word(std::string_view word) {
443*c8dee2aaSAndroid Build Coastguard Worker     static const THashSet<std::string_view> kReservedWords{
444*c8dee2aaSAndroid Build Coastguard Worker             // Used by SkSL:
445*c8dee2aaSAndroid Build Coastguard Worker             "FSIn",
446*c8dee2aaSAndroid Build Coastguard Worker             "FSOut",
447*c8dee2aaSAndroid Build Coastguard Worker             "VSIn",
448*c8dee2aaSAndroid Build Coastguard Worker             "VSOut",
449*c8dee2aaSAndroid Build Coastguard Worker             "CSIn",
450*c8dee2aaSAndroid Build Coastguard Worker             "_globalUniforms",
451*c8dee2aaSAndroid Build Coastguard Worker             "_GlobalUniforms",
452*c8dee2aaSAndroid Build Coastguard Worker             "_return",
453*c8dee2aaSAndroid Build Coastguard Worker             "_stageIn",
454*c8dee2aaSAndroid Build Coastguard Worker             "_stageOut",
455*c8dee2aaSAndroid Build Coastguard Worker             // Keywords: https://www.w3.org/TR/WGSL/#keyword-summary
456*c8dee2aaSAndroid Build Coastguard Worker             "alias",
457*c8dee2aaSAndroid Build Coastguard Worker             "break",
458*c8dee2aaSAndroid Build Coastguard Worker             "case",
459*c8dee2aaSAndroid Build Coastguard Worker             "const",
460*c8dee2aaSAndroid Build Coastguard Worker             "const_assert",
461*c8dee2aaSAndroid Build Coastguard Worker             "continue",
462*c8dee2aaSAndroid Build Coastguard Worker             "continuing",
463*c8dee2aaSAndroid Build Coastguard Worker             "default",
464*c8dee2aaSAndroid Build Coastguard Worker             "diagnostic",
465*c8dee2aaSAndroid Build Coastguard Worker             "discard",
466*c8dee2aaSAndroid Build Coastguard Worker             "else",
467*c8dee2aaSAndroid Build Coastguard Worker             "enable",
468*c8dee2aaSAndroid Build Coastguard Worker             "false",
469*c8dee2aaSAndroid Build Coastguard Worker             "fn",
470*c8dee2aaSAndroid Build Coastguard Worker             "for",
471*c8dee2aaSAndroid Build Coastguard Worker             "if",
472*c8dee2aaSAndroid Build Coastguard Worker             "let",
473*c8dee2aaSAndroid Build Coastguard Worker             "loop",
474*c8dee2aaSAndroid Build Coastguard Worker             "override",
475*c8dee2aaSAndroid Build Coastguard Worker             "requires",
476*c8dee2aaSAndroid Build Coastguard Worker             "return",
477*c8dee2aaSAndroid Build Coastguard Worker             "struct",
478*c8dee2aaSAndroid Build Coastguard Worker             "switch",
479*c8dee2aaSAndroid Build Coastguard Worker             "true",
480*c8dee2aaSAndroid Build Coastguard Worker             "var",
481*c8dee2aaSAndroid Build Coastguard Worker             "while",
482*c8dee2aaSAndroid Build Coastguard Worker             // Pre-declared types: https://www.w3.org/TR/WGSL/#predeclared-types
483*c8dee2aaSAndroid Build Coastguard Worker             "bool",
484*c8dee2aaSAndroid Build Coastguard Worker             "f16",
485*c8dee2aaSAndroid Build Coastguard Worker             "f32",
486*c8dee2aaSAndroid Build Coastguard Worker             "i32",
487*c8dee2aaSAndroid Build Coastguard Worker             "u32",
488*c8dee2aaSAndroid Build Coastguard Worker             // ... and pre-declared type generators:
489*c8dee2aaSAndroid Build Coastguard Worker             "array",
490*c8dee2aaSAndroid Build Coastguard Worker             "atomic",
491*c8dee2aaSAndroid Build Coastguard Worker             "mat2x2",
492*c8dee2aaSAndroid Build Coastguard Worker             "mat2x3",
493*c8dee2aaSAndroid Build Coastguard Worker             "mat2x4",
494*c8dee2aaSAndroid Build Coastguard Worker             "mat3x2",
495*c8dee2aaSAndroid Build Coastguard Worker             "mat3x3",
496*c8dee2aaSAndroid Build Coastguard Worker             "mat3x4",
497*c8dee2aaSAndroid Build Coastguard Worker             "mat4x2",
498*c8dee2aaSAndroid Build Coastguard Worker             "mat4x3",
499*c8dee2aaSAndroid Build Coastguard Worker             "mat4x4",
500*c8dee2aaSAndroid Build Coastguard Worker             "ptr",
501*c8dee2aaSAndroid Build Coastguard Worker             "texture_1d",
502*c8dee2aaSAndroid Build Coastguard Worker             "texture_2d",
503*c8dee2aaSAndroid Build Coastguard Worker             "texture_2d_array",
504*c8dee2aaSAndroid Build Coastguard Worker             "texture_3d",
505*c8dee2aaSAndroid Build Coastguard Worker             "texture_cube",
506*c8dee2aaSAndroid Build Coastguard Worker             "texture_cube_array",
507*c8dee2aaSAndroid Build Coastguard Worker             "texture_multisampled_2d",
508*c8dee2aaSAndroid Build Coastguard Worker             "texture_storage_1d",
509*c8dee2aaSAndroid Build Coastguard Worker             "texture_storage_2d",
510*c8dee2aaSAndroid Build Coastguard Worker             "texture_storage_2d_array",
511*c8dee2aaSAndroid Build Coastguard Worker             "texture_storage_3d",
512*c8dee2aaSAndroid Build Coastguard Worker             "vec2",
513*c8dee2aaSAndroid Build Coastguard Worker             "vec3",
514*c8dee2aaSAndroid Build Coastguard Worker             "vec4",
515*c8dee2aaSAndroid Build Coastguard Worker             // Pre-declared enumerants: https://www.w3.org/TR/WGSL/#predeclared-enumerants
516*c8dee2aaSAndroid Build Coastguard Worker             "read",
517*c8dee2aaSAndroid Build Coastguard Worker             "write",
518*c8dee2aaSAndroid Build Coastguard Worker             "read_write",
519*c8dee2aaSAndroid Build Coastguard Worker             "function",
520*c8dee2aaSAndroid Build Coastguard Worker             "private",
521*c8dee2aaSAndroid Build Coastguard Worker             "workgroup",
522*c8dee2aaSAndroid Build Coastguard Worker             "uniform",
523*c8dee2aaSAndroid Build Coastguard Worker             "storage",
524*c8dee2aaSAndroid Build Coastguard Worker             "perspective",
525*c8dee2aaSAndroid Build Coastguard Worker             "linear",
526*c8dee2aaSAndroid Build Coastguard Worker             "flat",
527*c8dee2aaSAndroid Build Coastguard Worker             "center",
528*c8dee2aaSAndroid Build Coastguard Worker             "centroid",
529*c8dee2aaSAndroid Build Coastguard Worker             "sample",
530*c8dee2aaSAndroid Build Coastguard Worker             "vertex_index",
531*c8dee2aaSAndroid Build Coastguard Worker             "instance_index",
532*c8dee2aaSAndroid Build Coastguard Worker             "position",
533*c8dee2aaSAndroid Build Coastguard Worker             "front_facing",
534*c8dee2aaSAndroid Build Coastguard Worker             "frag_depth",
535*c8dee2aaSAndroid Build Coastguard Worker             "local_invocation_id",
536*c8dee2aaSAndroid Build Coastguard Worker             "local_invocation_index",
537*c8dee2aaSAndroid Build Coastguard Worker             "global_invocation_id",
538*c8dee2aaSAndroid Build Coastguard Worker             "workgroup_id",
539*c8dee2aaSAndroid Build Coastguard Worker             "num_workgroups",
540*c8dee2aaSAndroid Build Coastguard Worker             "sample_index",
541*c8dee2aaSAndroid Build Coastguard Worker             "sample_mask",
542*c8dee2aaSAndroid Build Coastguard Worker             "rgba8unorm",
543*c8dee2aaSAndroid Build Coastguard Worker             "rgba8snorm",
544*c8dee2aaSAndroid Build Coastguard Worker             "rgba8uint",
545*c8dee2aaSAndroid Build Coastguard Worker             "rgba8sint",
546*c8dee2aaSAndroid Build Coastguard Worker             "rgba16uint",
547*c8dee2aaSAndroid Build Coastguard Worker             "rgba16sint",
548*c8dee2aaSAndroid Build Coastguard Worker             "rgba16float",
549*c8dee2aaSAndroid Build Coastguard Worker             "r32uint",
550*c8dee2aaSAndroid Build Coastguard Worker             "r32sint",
551*c8dee2aaSAndroid Build Coastguard Worker             "r32float",
552*c8dee2aaSAndroid Build Coastguard Worker             "rg32uint",
553*c8dee2aaSAndroid Build Coastguard Worker             "rg32sint",
554*c8dee2aaSAndroid Build Coastguard Worker             "rg32float",
555*c8dee2aaSAndroid Build Coastguard Worker             "rgba32uint",
556*c8dee2aaSAndroid Build Coastguard Worker             "rgba32sint",
557*c8dee2aaSAndroid Build Coastguard Worker             "rgba32float",
558*c8dee2aaSAndroid Build Coastguard Worker             "bgra8unorm",
559*c8dee2aaSAndroid Build Coastguard Worker             // Reserved words: https://www.w3.org/TR/WGSL/#reserved-words
560*c8dee2aaSAndroid Build Coastguard Worker             "_",
561*c8dee2aaSAndroid Build Coastguard Worker             "NULL",
562*c8dee2aaSAndroid Build Coastguard Worker             "Self",
563*c8dee2aaSAndroid Build Coastguard Worker             "abstract",
564*c8dee2aaSAndroid Build Coastguard Worker             "active",
565*c8dee2aaSAndroid Build Coastguard Worker             "alignas",
566*c8dee2aaSAndroid Build Coastguard Worker             "alignof",
567*c8dee2aaSAndroid Build Coastguard Worker             "as",
568*c8dee2aaSAndroid Build Coastguard Worker             "asm",
569*c8dee2aaSAndroid Build Coastguard Worker             "asm_fragment",
570*c8dee2aaSAndroid Build Coastguard Worker             "async",
571*c8dee2aaSAndroid Build Coastguard Worker             "attribute",
572*c8dee2aaSAndroid Build Coastguard Worker             "auto",
573*c8dee2aaSAndroid Build Coastguard Worker             "await",
574*c8dee2aaSAndroid Build Coastguard Worker             "become",
575*c8dee2aaSAndroid Build Coastguard Worker             "binding_array",
576*c8dee2aaSAndroid Build Coastguard Worker             "cast",
577*c8dee2aaSAndroid Build Coastguard Worker             "catch",
578*c8dee2aaSAndroid Build Coastguard Worker             "class",
579*c8dee2aaSAndroid Build Coastguard Worker             "co_await",
580*c8dee2aaSAndroid Build Coastguard Worker             "co_return",
581*c8dee2aaSAndroid Build Coastguard Worker             "co_yield",
582*c8dee2aaSAndroid Build Coastguard Worker             "coherent",
583*c8dee2aaSAndroid Build Coastguard Worker             "column_major",
584*c8dee2aaSAndroid Build Coastguard Worker             "common",
585*c8dee2aaSAndroid Build Coastguard Worker             "compile",
586*c8dee2aaSAndroid Build Coastguard Worker             "compile_fragment",
587*c8dee2aaSAndroid Build Coastguard Worker             "concept",
588*c8dee2aaSAndroid Build Coastguard Worker             "const_cast",
589*c8dee2aaSAndroid Build Coastguard Worker             "consteval",
590*c8dee2aaSAndroid Build Coastguard Worker             "constexpr",
591*c8dee2aaSAndroid Build Coastguard Worker             "constinit",
592*c8dee2aaSAndroid Build Coastguard Worker             "crate",
593*c8dee2aaSAndroid Build Coastguard Worker             "debugger",
594*c8dee2aaSAndroid Build Coastguard Worker             "decltype",
595*c8dee2aaSAndroid Build Coastguard Worker             "delete",
596*c8dee2aaSAndroid Build Coastguard Worker             "demote",
597*c8dee2aaSAndroid Build Coastguard Worker             "demote_to_helper",
598*c8dee2aaSAndroid Build Coastguard Worker             "do",
599*c8dee2aaSAndroid Build Coastguard Worker             "dynamic_cast",
600*c8dee2aaSAndroid Build Coastguard Worker             "enum",
601*c8dee2aaSAndroid Build Coastguard Worker             "explicit",
602*c8dee2aaSAndroid Build Coastguard Worker             "export",
603*c8dee2aaSAndroid Build Coastguard Worker             "extends",
604*c8dee2aaSAndroid Build Coastguard Worker             "extern",
605*c8dee2aaSAndroid Build Coastguard Worker             "external",
606*c8dee2aaSAndroid Build Coastguard Worker             "fallthrough",
607*c8dee2aaSAndroid Build Coastguard Worker             "filter",
608*c8dee2aaSAndroid Build Coastguard Worker             "final",
609*c8dee2aaSAndroid Build Coastguard Worker             "finally",
610*c8dee2aaSAndroid Build Coastguard Worker             "friend",
611*c8dee2aaSAndroid Build Coastguard Worker             "from",
612*c8dee2aaSAndroid Build Coastguard Worker             "fxgroup",
613*c8dee2aaSAndroid Build Coastguard Worker             "get",
614*c8dee2aaSAndroid Build Coastguard Worker             "goto",
615*c8dee2aaSAndroid Build Coastguard Worker             "groupshared",
616*c8dee2aaSAndroid Build Coastguard Worker             "highp",
617*c8dee2aaSAndroid Build Coastguard Worker             "impl",
618*c8dee2aaSAndroid Build Coastguard Worker             "implements",
619*c8dee2aaSAndroid Build Coastguard Worker             "import",
620*c8dee2aaSAndroid Build Coastguard Worker             "inline",
621*c8dee2aaSAndroid Build Coastguard Worker             "instanceof",
622*c8dee2aaSAndroid Build Coastguard Worker             "interface",
623*c8dee2aaSAndroid Build Coastguard Worker             "layout",
624*c8dee2aaSAndroid Build Coastguard Worker             "lowp",
625*c8dee2aaSAndroid Build Coastguard Worker             "macro",
626*c8dee2aaSAndroid Build Coastguard Worker             "macro_rules",
627*c8dee2aaSAndroid Build Coastguard Worker             "match",
628*c8dee2aaSAndroid Build Coastguard Worker             "mediump",
629*c8dee2aaSAndroid Build Coastguard Worker             "meta",
630*c8dee2aaSAndroid Build Coastguard Worker             "mod",
631*c8dee2aaSAndroid Build Coastguard Worker             "module",
632*c8dee2aaSAndroid Build Coastguard Worker             "move",
633*c8dee2aaSAndroid Build Coastguard Worker             "mut",
634*c8dee2aaSAndroid Build Coastguard Worker             "mutable",
635*c8dee2aaSAndroid Build Coastguard Worker             "namespace",
636*c8dee2aaSAndroid Build Coastguard Worker             "new",
637*c8dee2aaSAndroid Build Coastguard Worker             "nil",
638*c8dee2aaSAndroid Build Coastguard Worker             "noexcept",
639*c8dee2aaSAndroid Build Coastguard Worker             "noinline",
640*c8dee2aaSAndroid Build Coastguard Worker             "nointerpolation",
641*c8dee2aaSAndroid Build Coastguard Worker             "noperspective",
642*c8dee2aaSAndroid Build Coastguard Worker             "null",
643*c8dee2aaSAndroid Build Coastguard Worker             "nullptr",
644*c8dee2aaSAndroid Build Coastguard Worker             "of",
645*c8dee2aaSAndroid Build Coastguard Worker             "operator",
646*c8dee2aaSAndroid Build Coastguard Worker             "package",
647*c8dee2aaSAndroid Build Coastguard Worker             "packoffset",
648*c8dee2aaSAndroid Build Coastguard Worker             "partition",
649*c8dee2aaSAndroid Build Coastguard Worker             "pass",
650*c8dee2aaSAndroid Build Coastguard Worker             "patch",
651*c8dee2aaSAndroid Build Coastguard Worker             "pixelfragment",
652*c8dee2aaSAndroid Build Coastguard Worker             "precise",
653*c8dee2aaSAndroid Build Coastguard Worker             "precision",
654*c8dee2aaSAndroid Build Coastguard Worker             "premerge",
655*c8dee2aaSAndroid Build Coastguard Worker             "priv",
656*c8dee2aaSAndroid Build Coastguard Worker             "protected",
657*c8dee2aaSAndroid Build Coastguard Worker             "pub",
658*c8dee2aaSAndroid Build Coastguard Worker             "public",
659*c8dee2aaSAndroid Build Coastguard Worker             "readonly",
660*c8dee2aaSAndroid Build Coastguard Worker             "ref",
661*c8dee2aaSAndroid Build Coastguard Worker             "regardless",
662*c8dee2aaSAndroid Build Coastguard Worker             "register",
663*c8dee2aaSAndroid Build Coastguard Worker             "reinterpret_cast",
664*c8dee2aaSAndroid Build Coastguard Worker             "require",
665*c8dee2aaSAndroid Build Coastguard Worker             "resource",
666*c8dee2aaSAndroid Build Coastguard Worker             "restrict",
667*c8dee2aaSAndroid Build Coastguard Worker             "self",
668*c8dee2aaSAndroid Build Coastguard Worker             "set",
669*c8dee2aaSAndroid Build Coastguard Worker             "shared",
670*c8dee2aaSAndroid Build Coastguard Worker             "sizeof",
671*c8dee2aaSAndroid Build Coastguard Worker             "smooth",
672*c8dee2aaSAndroid Build Coastguard Worker             "snorm",
673*c8dee2aaSAndroid Build Coastguard Worker             "static",
674*c8dee2aaSAndroid Build Coastguard Worker             "static_assert",
675*c8dee2aaSAndroid Build Coastguard Worker             "static_cast",
676*c8dee2aaSAndroid Build Coastguard Worker             "std",
677*c8dee2aaSAndroid Build Coastguard Worker             "subroutine",
678*c8dee2aaSAndroid Build Coastguard Worker             "super",
679*c8dee2aaSAndroid Build Coastguard Worker             "target",
680*c8dee2aaSAndroid Build Coastguard Worker             "template",
681*c8dee2aaSAndroid Build Coastguard Worker             "this",
682*c8dee2aaSAndroid Build Coastguard Worker             "thread_local",
683*c8dee2aaSAndroid Build Coastguard Worker             "throw",
684*c8dee2aaSAndroid Build Coastguard Worker             "trait",
685*c8dee2aaSAndroid Build Coastguard Worker             "try",
686*c8dee2aaSAndroid Build Coastguard Worker             "type",
687*c8dee2aaSAndroid Build Coastguard Worker             "typedef",
688*c8dee2aaSAndroid Build Coastguard Worker             "typeid",
689*c8dee2aaSAndroid Build Coastguard Worker             "typename",
690*c8dee2aaSAndroid Build Coastguard Worker             "typeof",
691*c8dee2aaSAndroid Build Coastguard Worker             "union",
692*c8dee2aaSAndroid Build Coastguard Worker             "unless",
693*c8dee2aaSAndroid Build Coastguard Worker             "unorm",
694*c8dee2aaSAndroid Build Coastguard Worker             "unsafe",
695*c8dee2aaSAndroid Build Coastguard Worker             "unsized",
696*c8dee2aaSAndroid Build Coastguard Worker             "use",
697*c8dee2aaSAndroid Build Coastguard Worker             "using",
698*c8dee2aaSAndroid Build Coastguard Worker             "varying",
699*c8dee2aaSAndroid Build Coastguard Worker             "virtual",
700*c8dee2aaSAndroid Build Coastguard Worker             "volatile",
701*c8dee2aaSAndroid Build Coastguard Worker             "wgsl",
702*c8dee2aaSAndroid Build Coastguard Worker             "where",
703*c8dee2aaSAndroid Build Coastguard Worker             "with",
704*c8dee2aaSAndroid Build Coastguard Worker             "writeonly",
705*c8dee2aaSAndroid Build Coastguard Worker             "yield",
706*c8dee2aaSAndroid Build Coastguard Worker     };
707*c8dee2aaSAndroid Build Coastguard Worker 
708*c8dee2aaSAndroid Build Coastguard Worker     return kReservedWords.contains(word);
709*c8dee2aaSAndroid Build Coastguard Worker }
710*c8dee2aaSAndroid Build Coastguard Worker 
pipeline_struct_prefix(ProgramKind kind)711*c8dee2aaSAndroid Build Coastguard Worker std::string_view pipeline_struct_prefix(ProgramKind kind) {
712*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsVertex(kind)) {
713*c8dee2aaSAndroid Build Coastguard Worker         return "VS";
714*c8dee2aaSAndroid Build Coastguard Worker     }
715*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsFragment(kind)) {
716*c8dee2aaSAndroid Build Coastguard Worker         return "FS";
717*c8dee2aaSAndroid Build Coastguard Worker     }
718*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsCompute(kind)) {
719*c8dee2aaSAndroid Build Coastguard Worker         return "CS";
720*c8dee2aaSAndroid Build Coastguard Worker     }
721*c8dee2aaSAndroid Build Coastguard Worker     // Compute programs don't have stage-in/stage-out pipeline structs.
722*c8dee2aaSAndroid Build Coastguard Worker     return "";
723*c8dee2aaSAndroid Build Coastguard Worker }
724*c8dee2aaSAndroid Build Coastguard Worker 
address_space_to_str(PtrAddressSpace addressSpace)725*c8dee2aaSAndroid Build Coastguard Worker std::string_view address_space_to_str(PtrAddressSpace addressSpace) {
726*c8dee2aaSAndroid Build Coastguard Worker     switch (addressSpace) {
727*c8dee2aaSAndroid Build Coastguard Worker         case PtrAddressSpace::kFunction:
728*c8dee2aaSAndroid Build Coastguard Worker             return "function";
729*c8dee2aaSAndroid Build Coastguard Worker         case PtrAddressSpace::kPrivate:
730*c8dee2aaSAndroid Build Coastguard Worker             return "private";
731*c8dee2aaSAndroid Build Coastguard Worker         case PtrAddressSpace::kStorage:
732*c8dee2aaSAndroid Build Coastguard Worker             return "storage";
733*c8dee2aaSAndroid Build Coastguard Worker     }
734*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAIL("unsupported ptr address space");
735*c8dee2aaSAndroid Build Coastguard Worker     return "unsupported";
736*c8dee2aaSAndroid Build Coastguard Worker }
737*c8dee2aaSAndroid Build Coastguard Worker 
to_scalar_type(const Type & type)738*c8dee2aaSAndroid Build Coastguard Worker std::string_view to_scalar_type(const Type& type) {
739*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.typeKind() == Type::TypeKind::kScalar);
740*c8dee2aaSAndroid Build Coastguard Worker     switch (type.numberKind()) {
741*c8dee2aaSAndroid Build Coastguard Worker         // Floating-point numbers in WebGPU currently always have 32-bit footprint and
742*c8dee2aaSAndroid Build Coastguard Worker         // relaxed-precision is not supported without extensions. f32 is the only floating-point
743*c8dee2aaSAndroid Build Coastguard Worker         // number type in WGSL (see the discussion on https://github.com/gpuweb/gpuweb/issues/658).
744*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat:
745*c8dee2aaSAndroid Build Coastguard Worker             return "f32";
746*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kSigned:
747*c8dee2aaSAndroid Build Coastguard Worker             return "i32";
748*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kUnsigned:
749*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
750*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean:
751*c8dee2aaSAndroid Build Coastguard Worker             return "bool";
752*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kNonnumeric:
753*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
754*c8dee2aaSAndroid Build Coastguard Worker         default:
755*c8dee2aaSAndroid Build Coastguard Worker             break;
756*c8dee2aaSAndroid Build Coastguard Worker     }
757*c8dee2aaSAndroid Build Coastguard Worker     return type.name();
758*c8dee2aaSAndroid Build Coastguard Worker }
759*c8dee2aaSAndroid Build Coastguard Worker 
760*c8dee2aaSAndroid Build Coastguard Worker // Convert a SkSL type to a WGSL type. Handles all plain types except structure types
761*c8dee2aaSAndroid Build Coastguard Worker // (see https://www.w3.org/TR/WGSL/#plain-types-section).
to_wgsl_type(const Context & context,const Type & raw,const Layout * layout=nullptr)762*c8dee2aaSAndroid Build Coastguard Worker std::string to_wgsl_type(const Context& context, const Type& raw, const Layout* layout = nullptr) {
763*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = raw.resolve().scalarTypeForLiteral();
764*c8dee2aaSAndroid Build Coastguard Worker     switch (type.typeKind()) {
765*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kScalar:
766*c8dee2aaSAndroid Build Coastguard Worker             return std::string(to_scalar_type(type));
767*c8dee2aaSAndroid Build Coastguard Worker 
768*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kAtomic:
769*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(type.matches(*context.fTypes.fAtomicUInt));
770*c8dee2aaSAndroid Build Coastguard Worker             return "atomic<u32>";
771*c8dee2aaSAndroid Build Coastguard Worker 
772*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kVector: {
773*c8dee2aaSAndroid Build Coastguard Worker             std::string_view ct = to_scalar_type(type.componentType());
774*c8dee2aaSAndroid Build Coastguard Worker             return String::printf("vec%d<%.*s>", type.columns(), (int)ct.length(), ct.data());
775*c8dee2aaSAndroid Build Coastguard Worker         }
776*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kMatrix: {
777*c8dee2aaSAndroid Build Coastguard Worker             std::string_view ct = to_scalar_type(type.componentType());
778*c8dee2aaSAndroid Build Coastguard Worker             return String::printf("mat%dx%d<%.*s>",
779*c8dee2aaSAndroid Build Coastguard Worker                                   type.columns(), type.rows(), (int)ct.length(), ct.data());
780*c8dee2aaSAndroid Build Coastguard Worker         }
781*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kArray: {
782*c8dee2aaSAndroid Build Coastguard Worker             std::string result = "array<" + to_wgsl_type(context, type.componentType(), layout);
783*c8dee2aaSAndroid Build Coastguard Worker             if (!type.isUnsizedArray()) {
784*c8dee2aaSAndroid Build Coastguard Worker                 result += ", ";
785*c8dee2aaSAndroid Build Coastguard Worker                 result += std::to_string(type.columns());
786*c8dee2aaSAndroid Build Coastguard Worker             }
787*c8dee2aaSAndroid Build Coastguard Worker             return result + '>';
788*c8dee2aaSAndroid Build Coastguard Worker         }
789*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kTexture: {
790*c8dee2aaSAndroid Build Coastguard Worker             if (type.matches(*context.fTypes.fWriteOnlyTexture2D)) {
791*c8dee2aaSAndroid Build Coastguard Worker                 std::string result = "texture_storage_2d<";
792*c8dee2aaSAndroid Build Coastguard Worker                 // Write-only storage texture types require a pixel format, which is in the layout.
793*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(layout);
794*c8dee2aaSAndroid Build Coastguard Worker                 LayoutFlags pixelFormat = layout->fFlags & LayoutFlag::kAllPixelFormats;
795*c8dee2aaSAndroid Build Coastguard Worker                 switch (pixelFormat.value()) {
796*c8dee2aaSAndroid Build Coastguard Worker                     case (int)LayoutFlag::kRGBA8:
797*c8dee2aaSAndroid Build Coastguard Worker                         return result + "rgba8unorm, write>";
798*c8dee2aaSAndroid Build Coastguard Worker 
799*c8dee2aaSAndroid Build Coastguard Worker                     case (int)LayoutFlag::kRGBA32F:
800*c8dee2aaSAndroid Build Coastguard Worker                         return result + "rgba32float, write>";
801*c8dee2aaSAndroid Build Coastguard Worker 
802*c8dee2aaSAndroid Build Coastguard Worker                     case (int)LayoutFlag::kR32F:
803*c8dee2aaSAndroid Build Coastguard Worker                         return result + "r32float, write>";
804*c8dee2aaSAndroid Build Coastguard Worker 
805*c8dee2aaSAndroid Build Coastguard Worker                     default:
806*c8dee2aaSAndroid Build Coastguard Worker                         // The front-end should have rejected this.
807*c8dee2aaSAndroid Build Coastguard Worker                         return result + "write>";
808*c8dee2aaSAndroid Build Coastguard Worker                 }
809*c8dee2aaSAndroid Build Coastguard Worker             }
810*c8dee2aaSAndroid Build Coastguard Worker             if (type.matches(*context.fTypes.fReadOnlyTexture2D)) {
811*c8dee2aaSAndroid Build Coastguard Worker                 return "texture_2d<f32>";
812*c8dee2aaSAndroid Build Coastguard Worker             }
813*c8dee2aaSAndroid Build Coastguard Worker             break;
814*c8dee2aaSAndroid Build Coastguard Worker         }
815*c8dee2aaSAndroid Build Coastguard Worker         default:
816*c8dee2aaSAndroid Build Coastguard Worker             break;
817*c8dee2aaSAndroid Build Coastguard Worker     }
818*c8dee2aaSAndroid Build Coastguard Worker     return std::string(type.name());
819*c8dee2aaSAndroid Build Coastguard Worker }
820*c8dee2aaSAndroid Build Coastguard Worker 
to_ptr_type(const Context & context,const Type & type,const Layout * layout,PtrAddressSpace addressSpace=PtrAddressSpace::kFunction)821*c8dee2aaSAndroid Build Coastguard Worker std::string to_ptr_type(const Context& context,
822*c8dee2aaSAndroid Build Coastguard Worker                         const Type& type,
823*c8dee2aaSAndroid Build Coastguard Worker                         const Layout* layout,
824*c8dee2aaSAndroid Build Coastguard Worker                         PtrAddressSpace addressSpace = PtrAddressSpace::kFunction) {
825*c8dee2aaSAndroid Build Coastguard Worker     return "ptr<" + std::string(address_space_to_str(addressSpace)) + ", " +
826*c8dee2aaSAndroid Build Coastguard Worker            to_wgsl_type(context, type, layout) + '>';
827*c8dee2aaSAndroid Build Coastguard Worker }
828*c8dee2aaSAndroid Build Coastguard Worker 
wgsl_builtin_name(WGSLCodeGenerator::Builtin builtin)829*c8dee2aaSAndroid Build Coastguard Worker std::string_view wgsl_builtin_name(WGSLCodeGenerator::Builtin builtin) {
830*c8dee2aaSAndroid Build Coastguard Worker     using Builtin = WGSLCodeGenerator::Builtin;
831*c8dee2aaSAndroid Build Coastguard Worker     switch (builtin) {
832*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kVertexIndex:
833*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(vertex_index)";
834*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kInstanceIndex:
835*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(instance_index)";
836*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kPosition:
837*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(position)";
838*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLastFragColor:
839*c8dee2aaSAndroid Build Coastguard Worker             return "@color(0)";
840*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kFrontFacing:
841*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(front_facing)";
842*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleIndex:
843*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(sample_index)";
844*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kFragDepth:
845*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(frag_depth)";
846*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleMask:
847*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleMaskIn:
848*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(sample_mask)";
849*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLocalInvocationId:
850*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(local_invocation_id)";
851*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLocalInvocationIndex:
852*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(local_invocation_index)";
853*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kGlobalInvocationId:
854*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(global_invocation_id)";
855*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kWorkgroupId:
856*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(workgroup_id)";
857*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kNumWorkgroups:
858*c8dee2aaSAndroid Build Coastguard Worker             return "@builtin(num_workgroups)";
859*c8dee2aaSAndroid Build Coastguard Worker         default:
860*c8dee2aaSAndroid Build Coastguard Worker             break;
861*c8dee2aaSAndroid Build Coastguard Worker     }
862*c8dee2aaSAndroid Build Coastguard Worker 
863*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAIL("unsupported builtin");
864*c8dee2aaSAndroid Build Coastguard Worker     return "unsupported";
865*c8dee2aaSAndroid Build Coastguard Worker }
866*c8dee2aaSAndroid Build Coastguard Worker 
wgsl_builtin_type(WGSLCodeGenerator::Builtin builtin)867*c8dee2aaSAndroid Build Coastguard Worker std::string_view wgsl_builtin_type(WGSLCodeGenerator::Builtin builtin) {
868*c8dee2aaSAndroid Build Coastguard Worker     using Builtin = WGSLCodeGenerator::Builtin;
869*c8dee2aaSAndroid Build Coastguard Worker     switch (builtin) {
870*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kVertexIndex:
871*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
872*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kInstanceIndex:
873*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
874*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kPosition:
875*c8dee2aaSAndroid Build Coastguard Worker             return "vec4<f32>";
876*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLastFragColor:
877*c8dee2aaSAndroid Build Coastguard Worker             return "vec4<f32>";
878*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kFrontFacing:
879*c8dee2aaSAndroid Build Coastguard Worker             return "bool";
880*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleIndex:
881*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
882*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kFragDepth:
883*c8dee2aaSAndroid Build Coastguard Worker             return "f32";
884*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleMask:
885*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
886*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kSampleMaskIn:
887*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
888*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLocalInvocationId:
889*c8dee2aaSAndroid Build Coastguard Worker             return "vec3<u32>";
890*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kLocalInvocationIndex:
891*c8dee2aaSAndroid Build Coastguard Worker             return "u32";
892*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kGlobalInvocationId:
893*c8dee2aaSAndroid Build Coastguard Worker             return "vec3<u32>";
894*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kWorkgroupId:
895*c8dee2aaSAndroid Build Coastguard Worker             return "vec3<u32>";
896*c8dee2aaSAndroid Build Coastguard Worker         case Builtin::kNumWorkgroups:
897*c8dee2aaSAndroid Build Coastguard Worker             return "vec3<u32>";
898*c8dee2aaSAndroid Build Coastguard Worker         default:
899*c8dee2aaSAndroid Build Coastguard Worker             break;
900*c8dee2aaSAndroid Build Coastguard Worker     }
901*c8dee2aaSAndroid Build Coastguard Worker 
902*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAIL("unsupported builtin");
903*c8dee2aaSAndroid Build Coastguard Worker     return "unsupported";
904*c8dee2aaSAndroid Build Coastguard Worker }
905*c8dee2aaSAndroid Build Coastguard Worker 
906*c8dee2aaSAndroid Build Coastguard Worker // Some built-in variables have a type that differs from their SkSL counterpart (e.g. signed vs
907*c8dee2aaSAndroid Build Coastguard Worker // unsigned integer). We handle these cases with an explicit type conversion during a variable
908*c8dee2aaSAndroid Build Coastguard Worker // reference. Returns the WGSL type of the conversion target if conversion is needed, otherwise
909*c8dee2aaSAndroid Build Coastguard Worker // returns std::nullopt.
needs_builtin_type_conversion(const Variable & v)910*c8dee2aaSAndroid Build Coastguard Worker std::optional<std::string_view> needs_builtin_type_conversion(const Variable& v) {
911*c8dee2aaSAndroid Build Coastguard Worker     switch (v.layout().fBuiltin) {
912*c8dee2aaSAndroid Build Coastguard Worker         case SK_VERTEXID_BUILTIN:
913*c8dee2aaSAndroid Build Coastguard Worker         case SK_INSTANCEID_BUILTIN:
914*c8dee2aaSAndroid Build Coastguard Worker             return {"i32"};
915*c8dee2aaSAndroid Build Coastguard Worker         default:
916*c8dee2aaSAndroid Build Coastguard Worker             break;
917*c8dee2aaSAndroid Build Coastguard Worker     }
918*c8dee2aaSAndroid Build Coastguard Worker     return std::nullopt;
919*c8dee2aaSAndroid Build Coastguard Worker }
920*c8dee2aaSAndroid Build Coastguard Worker 
921*c8dee2aaSAndroid Build Coastguard Worker // Map a SkSL builtin flag to a WGSL builtin kind. Returns std::nullopt if `builtin` is not
922*c8dee2aaSAndroid Build Coastguard Worker // not supported for WGSL.
923*c8dee2aaSAndroid Build Coastguard Worker //
924*c8dee2aaSAndroid Build Coastguard Worker // Also see //src/sksl/sksl_vert.sksl and //src/sksl/sksl_frag.sksl for supported built-ins.
builtin_from_sksl_name(int builtin)925*c8dee2aaSAndroid Build Coastguard Worker std::optional<WGSLCodeGenerator::Builtin> builtin_from_sksl_name(int builtin) {
926*c8dee2aaSAndroid Build Coastguard Worker     using Builtin = WGSLCodeGenerator::Builtin;
927*c8dee2aaSAndroid Build Coastguard Worker     switch (builtin) {
928*c8dee2aaSAndroid Build Coastguard Worker         case SK_POSITION_BUILTIN:
929*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
930*c8dee2aaSAndroid Build Coastguard Worker         case SK_FRAGCOORD_BUILTIN:
931*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kPosition;
932*c8dee2aaSAndroid Build Coastguard Worker         case SK_VERTEXID_BUILTIN:
933*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kVertexIndex;
934*c8dee2aaSAndroid Build Coastguard Worker         case SK_INSTANCEID_BUILTIN:
935*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kInstanceIndex;
936*c8dee2aaSAndroid Build Coastguard Worker         case SK_LASTFRAGCOLOR_BUILTIN:
937*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kLastFragColor;
938*c8dee2aaSAndroid Build Coastguard Worker         case SK_CLOCKWISE_BUILTIN:
939*c8dee2aaSAndroid Build Coastguard Worker             // TODO(skia:13092): While `front_facing` is the corresponding built-in, it does not
940*c8dee2aaSAndroid Build Coastguard Worker             // imply a particular winding order. We correctly compute the face orientation based
941*c8dee2aaSAndroid Build Coastguard Worker             // on how Skia configured the render pipeline for all references to this built-in
942*c8dee2aaSAndroid Build Coastguard Worker             // variable (see `SkSL::Program::Interface::fRTFlipUniform`).
943*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kFrontFacing;
944*c8dee2aaSAndroid Build Coastguard Worker         case SK_SAMPLEMASKIN_BUILTIN:
945*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kSampleMaskIn;
946*c8dee2aaSAndroid Build Coastguard Worker         case SK_SAMPLEMASK_BUILTIN:
947*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kSampleMask;
948*c8dee2aaSAndroid Build Coastguard Worker         case SK_NUMWORKGROUPS_BUILTIN:
949*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kNumWorkgroups;
950*c8dee2aaSAndroid Build Coastguard Worker         case SK_WORKGROUPID_BUILTIN:
951*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kWorkgroupId;
952*c8dee2aaSAndroid Build Coastguard Worker         case SK_LOCALINVOCATIONID_BUILTIN:
953*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kLocalInvocationId;
954*c8dee2aaSAndroid Build Coastguard Worker         case SK_GLOBALINVOCATIONID_BUILTIN:
955*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kGlobalInvocationId;
956*c8dee2aaSAndroid Build Coastguard Worker         case SK_LOCALINVOCATIONINDEX_BUILTIN:
957*c8dee2aaSAndroid Build Coastguard Worker             return Builtin::kLocalInvocationIndex;
958*c8dee2aaSAndroid Build Coastguard Worker         default:
959*c8dee2aaSAndroid Build Coastguard Worker             break;
960*c8dee2aaSAndroid Build Coastguard Worker     }
961*c8dee2aaSAndroid Build Coastguard Worker     return std::nullopt;
962*c8dee2aaSAndroid Build Coastguard Worker }
963*c8dee2aaSAndroid Build Coastguard Worker 
delimiter_to_str(WGSLCodeGenerator::Delimiter delimiter)964*c8dee2aaSAndroid Build Coastguard Worker const char* delimiter_to_str(WGSLCodeGenerator::Delimiter delimiter) {
965*c8dee2aaSAndroid Build Coastguard Worker     using Delim = WGSLCodeGenerator::Delimiter;
966*c8dee2aaSAndroid Build Coastguard Worker     switch (delimiter) {
967*c8dee2aaSAndroid Build Coastguard Worker         case Delim::kComma:
968*c8dee2aaSAndroid Build Coastguard Worker             return ",";
969*c8dee2aaSAndroid Build Coastguard Worker         case Delim::kSemicolon:
970*c8dee2aaSAndroid Build Coastguard Worker             return ";";
971*c8dee2aaSAndroid Build Coastguard Worker         case Delim::kNone:
972*c8dee2aaSAndroid Build Coastguard Worker         default:
973*c8dee2aaSAndroid Build Coastguard Worker             break;
974*c8dee2aaSAndroid Build Coastguard Worker     }
975*c8dee2aaSAndroid Build Coastguard Worker     return "";
976*c8dee2aaSAndroid Build Coastguard Worker }
977*c8dee2aaSAndroid Build Coastguard Worker 
978*c8dee2aaSAndroid Build Coastguard Worker // FunctionDependencyResolver visits the IR tree rooted at a particular function definition and
979*c8dee2aaSAndroid Build Coastguard Worker // computes that function's dependencies on pipeline stage IO parameters. These are later used to
980*c8dee2aaSAndroid Build Coastguard Worker // synthesize arguments when writing out function definitions.
981*c8dee2aaSAndroid Build Coastguard Worker class FunctionDependencyResolver : public ProgramVisitor {
982*c8dee2aaSAndroid Build Coastguard Worker public:
983*c8dee2aaSAndroid Build Coastguard Worker     using Deps = WGSLFunctionDependencies;
984*c8dee2aaSAndroid Build Coastguard Worker     using DepsMap = WGSLCodeGenerator::ProgramRequirements::DepsMap;
985*c8dee2aaSAndroid Build Coastguard Worker 
FunctionDependencyResolver(const Program * p,const FunctionDeclaration * f,DepsMap * programDependencyMap)986*c8dee2aaSAndroid Build Coastguard Worker     FunctionDependencyResolver(const Program* p,
987*c8dee2aaSAndroid Build Coastguard Worker                                const FunctionDeclaration* f,
988*c8dee2aaSAndroid Build Coastguard Worker                                DepsMap* programDependencyMap)
989*c8dee2aaSAndroid Build Coastguard Worker             : fProgram(p), fFunction(f), fDependencyMap(programDependencyMap) {}
990*c8dee2aaSAndroid Build Coastguard Worker 
resolve()991*c8dee2aaSAndroid Build Coastguard Worker     Deps resolve() {
992*c8dee2aaSAndroid Build Coastguard Worker         fDeps = WGSLFunctionDependency::kNone;
993*c8dee2aaSAndroid Build Coastguard Worker         this->visit(*fProgram);
994*c8dee2aaSAndroid Build Coastguard Worker         return fDeps;
995*c8dee2aaSAndroid Build Coastguard Worker     }
996*c8dee2aaSAndroid Build Coastguard Worker 
997*c8dee2aaSAndroid Build Coastguard Worker private:
visitProgramElement(const ProgramElement & p)998*c8dee2aaSAndroid Build Coastguard Worker     bool visitProgramElement(const ProgramElement& p) override {
999*c8dee2aaSAndroid Build Coastguard Worker         // Only visit the program that matches the requested function.
1000*c8dee2aaSAndroid Build Coastguard Worker         if (p.is<FunctionDefinition>() && &p.as<FunctionDefinition>().declaration() == fFunction) {
1001*c8dee2aaSAndroid Build Coastguard Worker             return ProgramVisitor::visitProgramElement(p);
1002*c8dee2aaSAndroid Build Coastguard Worker         }
1003*c8dee2aaSAndroid Build Coastguard Worker         // Continue visiting other program elements.
1004*c8dee2aaSAndroid Build Coastguard Worker         return false;
1005*c8dee2aaSAndroid Build Coastguard Worker     }
1006*c8dee2aaSAndroid Build Coastguard Worker 
visitExpression(const Expression & e)1007*c8dee2aaSAndroid Build Coastguard Worker     bool visitExpression(const Expression& e) override {
1008*c8dee2aaSAndroid Build Coastguard Worker         if (e.is<VariableReference>()) {
1009*c8dee2aaSAndroid Build Coastguard Worker             const VariableReference& v = e.as<VariableReference>();
1010*c8dee2aaSAndroid Build Coastguard Worker             if (v.variable()->storage() == Variable::Storage::kGlobal) {
1011*c8dee2aaSAndroid Build Coastguard Worker                 ModifierFlags flags = v.variable()->modifierFlags();
1012*c8dee2aaSAndroid Build Coastguard Worker                 if (flags & ModifierFlag::kIn) {
1013*c8dee2aaSAndroid Build Coastguard Worker                     fDeps |= WGSLFunctionDependency::kPipelineInputs;
1014*c8dee2aaSAndroid Build Coastguard Worker                 }
1015*c8dee2aaSAndroid Build Coastguard Worker                 if (flags & ModifierFlag::kOut) {
1016*c8dee2aaSAndroid Build Coastguard Worker                     fDeps |= WGSLFunctionDependency::kPipelineOutputs;
1017*c8dee2aaSAndroid Build Coastguard Worker                 }
1018*c8dee2aaSAndroid Build Coastguard Worker             }
1019*c8dee2aaSAndroid Build Coastguard Worker         } else if (e.is<FunctionCall>()) {
1020*c8dee2aaSAndroid Build Coastguard Worker             // The current function that we're processing (`fFunction`) inherits the dependencies of
1021*c8dee2aaSAndroid Build Coastguard Worker             // functions that it makes calls to, because the pipeline stage IO parameters need to be
1022*c8dee2aaSAndroid Build Coastguard Worker             // passed down as an argument.
1023*c8dee2aaSAndroid Build Coastguard Worker             const FunctionCall& callee = e.as<FunctionCall>();
1024*c8dee2aaSAndroid Build Coastguard Worker 
1025*c8dee2aaSAndroid Build Coastguard Worker             // Don't process a function again if we have already resolved it.
1026*c8dee2aaSAndroid Build Coastguard Worker             Deps* found = fDependencyMap->find(&callee.function());
1027*c8dee2aaSAndroid Build Coastguard Worker             if (found) {
1028*c8dee2aaSAndroid Build Coastguard Worker                 fDeps |= *found;
1029*c8dee2aaSAndroid Build Coastguard Worker             } else {
1030*c8dee2aaSAndroid Build Coastguard Worker                 // Store the dependencies that have been discovered for the current function so far.
1031*c8dee2aaSAndroid Build Coastguard Worker                 // If `callee` directly or indirectly calls the current function, then this value
1032*c8dee2aaSAndroid Build Coastguard Worker                 // will prevent an infinite recursion.
1033*c8dee2aaSAndroid Build Coastguard Worker                 fDependencyMap->set(fFunction, fDeps);
1034*c8dee2aaSAndroid Build Coastguard Worker 
1035*c8dee2aaSAndroid Build Coastguard Worker                 // Separately traverse the called function's definition and determine its
1036*c8dee2aaSAndroid Build Coastguard Worker                 // dependencies.
1037*c8dee2aaSAndroid Build Coastguard Worker                 FunctionDependencyResolver resolver(fProgram, &callee.function(), fDependencyMap);
1038*c8dee2aaSAndroid Build Coastguard Worker                 Deps calleeDeps = resolver.resolve();
1039*c8dee2aaSAndroid Build Coastguard Worker 
1040*c8dee2aaSAndroid Build Coastguard Worker                 // Store the callee's dependencies in the global map to avoid processing
1041*c8dee2aaSAndroid Build Coastguard Worker                 // the function again for future calls.
1042*c8dee2aaSAndroid Build Coastguard Worker                 fDependencyMap->set(&callee.function(), calleeDeps);
1043*c8dee2aaSAndroid Build Coastguard Worker 
1044*c8dee2aaSAndroid Build Coastguard Worker                 // Add to the current function's dependencies.
1045*c8dee2aaSAndroid Build Coastguard Worker                 fDeps |= calleeDeps;
1046*c8dee2aaSAndroid Build Coastguard Worker             }
1047*c8dee2aaSAndroid Build Coastguard Worker         }
1048*c8dee2aaSAndroid Build Coastguard Worker         return ProgramVisitor::visitExpression(e);
1049*c8dee2aaSAndroid Build Coastguard Worker     }
1050*c8dee2aaSAndroid Build Coastguard Worker 
1051*c8dee2aaSAndroid Build Coastguard Worker     const Program* const fProgram;
1052*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration* const fFunction;
1053*c8dee2aaSAndroid Build Coastguard Worker     DepsMap* const fDependencyMap;
1054*c8dee2aaSAndroid Build Coastguard Worker     Deps fDeps = WGSLFunctionDependency::kNone;
1055*c8dee2aaSAndroid Build Coastguard Worker };
1056*c8dee2aaSAndroid Build Coastguard Worker 
resolve_program_requirements(const Program * program)1057*c8dee2aaSAndroid Build Coastguard Worker WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(const Program* program) {
1058*c8dee2aaSAndroid Build Coastguard Worker     WGSLCodeGenerator::ProgramRequirements requirements;
1059*c8dee2aaSAndroid Build Coastguard Worker 
1060*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program->elements()) {
1061*c8dee2aaSAndroid Build Coastguard Worker         switch (e->kind()) {
1062*c8dee2aaSAndroid Build Coastguard Worker             case ProgramElement::Kind::kFunction: {
1063*c8dee2aaSAndroid Build Coastguard Worker                 const FunctionDeclaration& decl = e->as<FunctionDefinition>().declaration();
1064*c8dee2aaSAndroid Build Coastguard Worker 
1065*c8dee2aaSAndroid Build Coastguard Worker                 FunctionDependencyResolver resolver(program, &decl, &requirements.fDependencies);
1066*c8dee2aaSAndroid Build Coastguard Worker                 requirements.fDependencies.set(&decl, resolver.resolve());
1067*c8dee2aaSAndroid Build Coastguard Worker                 break;
1068*c8dee2aaSAndroid Build Coastguard Worker             }
1069*c8dee2aaSAndroid Build Coastguard Worker             case ProgramElement::Kind::kGlobalVar: {
1070*c8dee2aaSAndroid Build Coastguard Worker                 const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
1071*c8dee2aaSAndroid Build Coastguard Worker                 if (decl.varDeclaration().var()->modifierFlags().isPixelLocal()) {
1072*c8dee2aaSAndroid Build Coastguard Worker                     requirements.fPixelLocalExtension = true;
1073*c8dee2aaSAndroid Build Coastguard Worker                 }
1074*c8dee2aaSAndroid Build Coastguard Worker                 break;
1075*c8dee2aaSAndroid Build Coastguard Worker             }
1076*c8dee2aaSAndroid Build Coastguard Worker             default:
1077*c8dee2aaSAndroid Build Coastguard Worker                 break;
1078*c8dee2aaSAndroid Build Coastguard Worker         }
1079*c8dee2aaSAndroid Build Coastguard Worker     }
1080*c8dee2aaSAndroid Build Coastguard Worker 
1081*c8dee2aaSAndroid Build Coastguard Worker     return requirements;
1082*c8dee2aaSAndroid Build Coastguard Worker }
1083*c8dee2aaSAndroid Build Coastguard Worker 
collect_pipeline_io_vars(const Program * program,TArray<const Variable * > * ioVars,ModifierFlag ioType)1084*c8dee2aaSAndroid Build Coastguard Worker void collect_pipeline_io_vars(const Program* program,
1085*c8dee2aaSAndroid Build Coastguard Worker                               TArray<const Variable*>* ioVars,
1086*c8dee2aaSAndroid Build Coastguard Worker                               ModifierFlag ioType) {
1087*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program->elements()) {
1088*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<GlobalVarDeclaration>()) {
1089*c8dee2aaSAndroid Build Coastguard Worker             const Variable* v = e->as<GlobalVarDeclaration>().varDeclaration().var();
1090*c8dee2aaSAndroid Build Coastguard Worker             if (v->modifierFlags() & ioType) {
1091*c8dee2aaSAndroid Build Coastguard Worker                 ioVars->push_back(v);
1092*c8dee2aaSAndroid Build Coastguard Worker             }
1093*c8dee2aaSAndroid Build Coastguard Worker         } else if (e->is<InterfaceBlock>()) {
1094*c8dee2aaSAndroid Build Coastguard Worker             const Variable* v = e->as<InterfaceBlock>().var();
1095*c8dee2aaSAndroid Build Coastguard Worker             if (v->modifierFlags() & ioType) {
1096*c8dee2aaSAndroid Build Coastguard Worker                 ioVars->push_back(v);
1097*c8dee2aaSAndroid Build Coastguard Worker             }
1098*c8dee2aaSAndroid Build Coastguard Worker         }
1099*c8dee2aaSAndroid Build Coastguard Worker     }
1100*c8dee2aaSAndroid Build Coastguard Worker }
1101*c8dee2aaSAndroid Build Coastguard Worker 
is_in_global_uniforms(const Variable & var)1102*c8dee2aaSAndroid Build Coastguard Worker bool is_in_global_uniforms(const Variable& var) {
1103*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(var.storage() == VariableStorage::kGlobal);
1104*c8dee2aaSAndroid Build Coastguard Worker     return  var.modifierFlags().isUniform() &&
1105*c8dee2aaSAndroid Build Coastguard Worker            !var.type().isOpaque() &&
1106*c8dee2aaSAndroid Build Coastguard Worker            !var.interfaceBlock();
1107*c8dee2aaSAndroid Build Coastguard Worker }
1108*c8dee2aaSAndroid Build Coastguard Worker 
1109*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
1110*c8dee2aaSAndroid Build Coastguard Worker 
1111*c8dee2aaSAndroid Build Coastguard Worker class WGSLCodeGenerator::LValue {
1112*c8dee2aaSAndroid Build Coastguard Worker public:
1113*c8dee2aaSAndroid Build Coastguard Worker     virtual ~LValue() = default;
1114*c8dee2aaSAndroid Build Coastguard Worker 
1115*c8dee2aaSAndroid Build Coastguard Worker     // Returns a WGSL expression that loads from the lvalue with no side effects.
1116*c8dee2aaSAndroid Build Coastguard Worker     // (e.g. `array[index].field`)
1117*c8dee2aaSAndroid Build Coastguard Worker     virtual std::string load() = 0;
1118*c8dee2aaSAndroid Build Coastguard Worker 
1119*c8dee2aaSAndroid Build Coastguard Worker     // Returns a WGSL statement that stores into the lvalue with no side effects.
1120*c8dee2aaSAndroid Build Coastguard Worker     // (e.g. `array[index].field = the_passed_in_value_string;`)
1121*c8dee2aaSAndroid Build Coastguard Worker     virtual std::string store(const std::string& value) = 0;
1122*c8dee2aaSAndroid Build Coastguard Worker };
1123*c8dee2aaSAndroid Build Coastguard Worker 
1124*c8dee2aaSAndroid Build Coastguard Worker class WGSLCodeGenerator::PointerLValue : public WGSLCodeGenerator::LValue {
1125*c8dee2aaSAndroid Build Coastguard Worker public:
1126*c8dee2aaSAndroid Build Coastguard Worker     // `name` must be a WGSL expression with no side-effects, which we can safely take the address
1127*c8dee2aaSAndroid Build Coastguard Worker     // of. (e.g. `array[index].field` would be valid, but `array[Func()]` or `vector.x` are not.)
PointerLValue(std::string name)1128*c8dee2aaSAndroid Build Coastguard Worker     PointerLValue(std::string name) : fName(std::move(name)) {}
1129*c8dee2aaSAndroid Build Coastguard Worker 
load()1130*c8dee2aaSAndroid Build Coastguard Worker     std::string load() override {
1131*c8dee2aaSAndroid Build Coastguard Worker         return fName;
1132*c8dee2aaSAndroid Build Coastguard Worker     }
1133*c8dee2aaSAndroid Build Coastguard Worker 
store(const std::string & value)1134*c8dee2aaSAndroid Build Coastguard Worker     std::string store(const std::string& value) override {
1135*c8dee2aaSAndroid Build Coastguard Worker         return fName + " = " + value + ";";
1136*c8dee2aaSAndroid Build Coastguard Worker     }
1137*c8dee2aaSAndroid Build Coastguard Worker 
1138*c8dee2aaSAndroid Build Coastguard Worker private:
1139*c8dee2aaSAndroid Build Coastguard Worker     std::string fName;
1140*c8dee2aaSAndroid Build Coastguard Worker };
1141*c8dee2aaSAndroid Build Coastguard Worker 
1142*c8dee2aaSAndroid Build Coastguard Worker class WGSLCodeGenerator::VectorComponentLValue : public WGSLCodeGenerator::LValue {
1143*c8dee2aaSAndroid Build Coastguard Worker public:
1144*c8dee2aaSAndroid Build Coastguard Worker     // `name` must be a WGSL expression with no side-effects that points to a single component of a
1145*c8dee2aaSAndroid Build Coastguard Worker     // WGSL vector.
VectorComponentLValue(std::string name)1146*c8dee2aaSAndroid Build Coastguard Worker     VectorComponentLValue(std::string name) : fName(std::move(name)) {}
1147*c8dee2aaSAndroid Build Coastguard Worker 
load()1148*c8dee2aaSAndroid Build Coastguard Worker     std::string load() override {
1149*c8dee2aaSAndroid Build Coastguard Worker         return fName;
1150*c8dee2aaSAndroid Build Coastguard Worker     }
1151*c8dee2aaSAndroid Build Coastguard Worker 
store(const std::string & value)1152*c8dee2aaSAndroid Build Coastguard Worker     std::string store(const std::string& value) override {
1153*c8dee2aaSAndroid Build Coastguard Worker         return fName + " = " + value + ";";
1154*c8dee2aaSAndroid Build Coastguard Worker     }
1155*c8dee2aaSAndroid Build Coastguard Worker 
1156*c8dee2aaSAndroid Build Coastguard Worker private:
1157*c8dee2aaSAndroid Build Coastguard Worker     std::string fName;
1158*c8dee2aaSAndroid Build Coastguard Worker };
1159*c8dee2aaSAndroid Build Coastguard Worker 
1160*c8dee2aaSAndroid Build Coastguard Worker class WGSLCodeGenerator::SwizzleLValue : public WGSLCodeGenerator::LValue {
1161*c8dee2aaSAndroid Build Coastguard Worker public:
1162*c8dee2aaSAndroid Build Coastguard Worker     // `name` must be a WGSL expression with no side-effects that points to a WGSL vector.
SwizzleLValue(const Context & ctx,std::string name,const Type & t,const ComponentArray & c)1163*c8dee2aaSAndroid Build Coastguard Worker     SwizzleLValue(const Context& ctx, std::string name, const Type& t, const ComponentArray& c)
1164*c8dee2aaSAndroid Build Coastguard Worker             : fContext(ctx)
1165*c8dee2aaSAndroid Build Coastguard Worker             , fName(std::move(name))
1166*c8dee2aaSAndroid Build Coastguard Worker             , fType(t)
1167*c8dee2aaSAndroid Build Coastguard Worker             , fComponents(c) {
1168*c8dee2aaSAndroid Build Coastguard Worker         // If the component array doesn't cover the entire value, we need to create masks for
1169*c8dee2aaSAndroid Build Coastguard Worker         // writing back into the lvalue. For example, if the type is vec4 and the component array
1170*c8dee2aaSAndroid Build Coastguard Worker         // holds `zx`, a GLSL assignment would look like:
1171*c8dee2aaSAndroid Build Coastguard Worker         //     name.zx = new_value;
1172*c8dee2aaSAndroid Build Coastguard Worker         //
1173*c8dee2aaSAndroid Build Coastguard Worker         // The equivalent WGSL assignment statement would look like:
1174*c8dee2aaSAndroid Build Coastguard Worker         //     name = vec4<f32>(new_value, name.xw).yzxw;
1175*c8dee2aaSAndroid Build Coastguard Worker         //
1176*c8dee2aaSAndroid Build Coastguard Worker         // This replaces name.zy with new_value.xy, and leaves name.xw at their original values.
1177*c8dee2aaSAndroid Build Coastguard Worker         // By convention, we always put the new value first and the original values second; it might
1178*c8dee2aaSAndroid Build Coastguard Worker         // be possible to find better arrangements which simplify the assignment overall, but we
1179*c8dee2aaSAndroid Build Coastguard Worker         // don't attempt this.
1180*c8dee2aaSAndroid Build Coastguard Worker         int fullSlotCount = fType.slotCount();
1181*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fullSlotCount <= 4);
1182*c8dee2aaSAndroid Build Coastguard Worker 
1183*c8dee2aaSAndroid Build Coastguard Worker         // First, see which components are used.
1184*c8dee2aaSAndroid Build Coastguard Worker         // The assignment swizzle must not reuse components.
1185*c8dee2aaSAndroid Build Coastguard Worker         bool used[4] = {};
1186*c8dee2aaSAndroid Build Coastguard Worker         for (int8_t component : fComponents) {
1187*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!used[component]);
1188*c8dee2aaSAndroid Build Coastguard Worker             used[component] = true;
1189*c8dee2aaSAndroid Build Coastguard Worker         }
1190*c8dee2aaSAndroid Build Coastguard Worker 
1191*c8dee2aaSAndroid Build Coastguard Worker         // Any untouched components will need to be fetched from the original value.
1192*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < fullSlotCount; ++index) {
1193*c8dee2aaSAndroid Build Coastguard Worker             if (!used[index]) {
1194*c8dee2aaSAndroid Build Coastguard Worker                 fUntouchedComponents.push_back(index);
1195*c8dee2aaSAndroid Build Coastguard Worker             }
1196*c8dee2aaSAndroid Build Coastguard Worker         }
1197*c8dee2aaSAndroid Build Coastguard Worker 
1198*c8dee2aaSAndroid Build Coastguard Worker         // The reintegration swizzle needs to move the components back into their proper slots.
1199*c8dee2aaSAndroid Build Coastguard Worker         fReintegrationSwizzle.resize(fullSlotCount);
1200*c8dee2aaSAndroid Build Coastguard Worker         int reintegrateIndex = 0;
1201*c8dee2aaSAndroid Build Coastguard Worker 
1202*c8dee2aaSAndroid Build Coastguard Worker         // This refills the untouched slots with the original values.
1203*c8dee2aaSAndroid Build Coastguard Worker         auto refillUntouchedSlots = [&] {
1204*c8dee2aaSAndroid Build Coastguard Worker             for (int index = 0; index < fullSlotCount; ++index) {
1205*c8dee2aaSAndroid Build Coastguard Worker                 if (!used[index]) {
1206*c8dee2aaSAndroid Build Coastguard Worker                     fReintegrationSwizzle[index] = reintegrateIndex++;
1207*c8dee2aaSAndroid Build Coastguard Worker                 }
1208*c8dee2aaSAndroid Build Coastguard Worker             }
1209*c8dee2aaSAndroid Build Coastguard Worker         };
1210*c8dee2aaSAndroid Build Coastguard Worker 
1211*c8dee2aaSAndroid Build Coastguard Worker         // This places the new-value components into the proper slots.
1212*c8dee2aaSAndroid Build Coastguard Worker         auto insertNewValuesIntoSlots = [&] {
1213*c8dee2aaSAndroid Build Coastguard Worker             for (int index = 0; index < fComponents.size(); ++index) {
1214*c8dee2aaSAndroid Build Coastguard Worker                 fReintegrationSwizzle[fComponents[index]] = reintegrateIndex++;
1215*c8dee2aaSAndroid Build Coastguard Worker             }
1216*c8dee2aaSAndroid Build Coastguard Worker         };
1217*c8dee2aaSAndroid Build Coastguard Worker 
1218*c8dee2aaSAndroid Build Coastguard Worker         // When reintegrating the untouched and new values, if the `x` slot is overwritten, we
1219*c8dee2aaSAndroid Build Coastguard Worker         // reintegrate the new value first. Otherwise, we reintegrate the original value first.
1220*c8dee2aaSAndroid Build Coastguard Worker         // This increases our odds of getting an identity swizzle for the reintegration.
1221*c8dee2aaSAndroid Build Coastguard Worker         if (used[0]) {
1222*c8dee2aaSAndroid Build Coastguard Worker             fReintegrateNewValueFirst = true;
1223*c8dee2aaSAndroid Build Coastguard Worker             insertNewValuesIntoSlots();
1224*c8dee2aaSAndroid Build Coastguard Worker             refillUntouchedSlots();
1225*c8dee2aaSAndroid Build Coastguard Worker         } else {
1226*c8dee2aaSAndroid Build Coastguard Worker             fReintegrateNewValueFirst = false;
1227*c8dee2aaSAndroid Build Coastguard Worker             refillUntouchedSlots();
1228*c8dee2aaSAndroid Build Coastguard Worker             insertNewValuesIntoSlots();
1229*c8dee2aaSAndroid Build Coastguard Worker         }
1230*c8dee2aaSAndroid Build Coastguard Worker     }
1231*c8dee2aaSAndroid Build Coastguard Worker 
load()1232*c8dee2aaSAndroid Build Coastguard Worker     std::string load() override {
1233*c8dee2aaSAndroid Build Coastguard Worker         return fName + "." + Swizzle::MaskString(fComponents);
1234*c8dee2aaSAndroid Build Coastguard Worker     }
1235*c8dee2aaSAndroid Build Coastguard Worker 
store(const std::string & value)1236*c8dee2aaSAndroid Build Coastguard Worker     std::string store(const std::string& value) override {
1237*c8dee2aaSAndroid Build Coastguard Worker         // `variable = `
1238*c8dee2aaSAndroid Build Coastguard Worker         std::string result = fName;
1239*c8dee2aaSAndroid Build Coastguard Worker         result += " = ";
1240*c8dee2aaSAndroid Build Coastguard Worker 
1241*c8dee2aaSAndroid Build Coastguard Worker         if (fUntouchedComponents.empty()) {
1242*c8dee2aaSAndroid Build Coastguard Worker             // `(new_value);`
1243*c8dee2aaSAndroid Build Coastguard Worker             result += '(';
1244*c8dee2aaSAndroid Build Coastguard Worker             result += value;
1245*c8dee2aaSAndroid Build Coastguard Worker             result += ")";
1246*c8dee2aaSAndroid Build Coastguard Worker         } else if (fReintegrateNewValueFirst) {
1247*c8dee2aaSAndroid Build Coastguard Worker             // `vec4<f32>((new_value), `
1248*c8dee2aaSAndroid Build Coastguard Worker             result += to_wgsl_type(fContext, fType);
1249*c8dee2aaSAndroid Build Coastguard Worker             result += "((";
1250*c8dee2aaSAndroid Build Coastguard Worker             result += value;
1251*c8dee2aaSAndroid Build Coastguard Worker             result += "), ";
1252*c8dee2aaSAndroid Build Coastguard Worker 
1253*c8dee2aaSAndroid Build Coastguard Worker             // `variable.yz)`
1254*c8dee2aaSAndroid Build Coastguard Worker             result += fName;
1255*c8dee2aaSAndroid Build Coastguard Worker             result += '.';
1256*c8dee2aaSAndroid Build Coastguard Worker             result += Swizzle::MaskString(fUntouchedComponents);
1257*c8dee2aaSAndroid Build Coastguard Worker             result += ')';
1258*c8dee2aaSAndroid Build Coastguard Worker         } else {
1259*c8dee2aaSAndroid Build Coastguard Worker             // `vec4<f32>(variable.yz`
1260*c8dee2aaSAndroid Build Coastguard Worker             result += to_wgsl_type(fContext, fType);
1261*c8dee2aaSAndroid Build Coastguard Worker             result += '(';
1262*c8dee2aaSAndroid Build Coastguard Worker             result += fName;
1263*c8dee2aaSAndroid Build Coastguard Worker             result += '.';
1264*c8dee2aaSAndroid Build Coastguard Worker             result += Swizzle::MaskString(fUntouchedComponents);
1265*c8dee2aaSAndroid Build Coastguard Worker 
1266*c8dee2aaSAndroid Build Coastguard Worker             // `, (new_value))`
1267*c8dee2aaSAndroid Build Coastguard Worker             result += ", (";
1268*c8dee2aaSAndroid Build Coastguard Worker             result += value;
1269*c8dee2aaSAndroid Build Coastguard Worker             result += "))";
1270*c8dee2aaSAndroid Build Coastguard Worker         }
1271*c8dee2aaSAndroid Build Coastguard Worker 
1272*c8dee2aaSAndroid Build Coastguard Worker         if (!Swizzle::IsIdentity(fReintegrationSwizzle)) {
1273*c8dee2aaSAndroid Build Coastguard Worker             // `.wzyx`
1274*c8dee2aaSAndroid Build Coastguard Worker             result += '.';
1275*c8dee2aaSAndroid Build Coastguard Worker             result += Swizzle::MaskString(fReintegrationSwizzle);
1276*c8dee2aaSAndroid Build Coastguard Worker         }
1277*c8dee2aaSAndroid Build Coastguard Worker 
1278*c8dee2aaSAndroid Build Coastguard Worker         return result + ';';
1279*c8dee2aaSAndroid Build Coastguard Worker     }
1280*c8dee2aaSAndroid Build Coastguard Worker 
1281*c8dee2aaSAndroid Build Coastguard Worker private:
1282*c8dee2aaSAndroid Build Coastguard Worker     const Context& fContext;
1283*c8dee2aaSAndroid Build Coastguard Worker     std::string fName;
1284*c8dee2aaSAndroid Build Coastguard Worker     const Type& fType;
1285*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray fComponents;
1286*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray fUntouchedComponents;
1287*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray fReintegrationSwizzle;
1288*c8dee2aaSAndroid Build Coastguard Worker     bool fReintegrateNewValueFirst = false;
1289*c8dee2aaSAndroid Build Coastguard Worker };
1290*c8dee2aaSAndroid Build Coastguard Worker 
generateCode()1291*c8dee2aaSAndroid Build Coastguard Worker bool WGSLCodeGenerator::generateCode() {
1292*c8dee2aaSAndroid Build Coastguard Worker     // The resources of a WGSL program are structured in the following way:
1293*c8dee2aaSAndroid Build Coastguard Worker     // - Stage attribute inputs and outputs are bundled inside synthetic structs called
1294*c8dee2aaSAndroid Build Coastguard Worker     //   VSIn/VSOut/FSIn/FSOut/CSIn.
1295*c8dee2aaSAndroid Build Coastguard Worker     // - All uniform and storage type resources are declared in global scope.
1296*c8dee2aaSAndroid Build Coastguard Worker     this->preprocessProgram();
1297*c8dee2aaSAndroid Build Coastguard Worker 
1298*c8dee2aaSAndroid Build Coastguard Worker     {
1299*c8dee2aaSAndroid Build Coastguard Worker         AutoOutputStream outputToHeader(this, &fHeader, &fIndentation);
1300*c8dee2aaSAndroid Build Coastguard Worker         this->writeEnables();
1301*c8dee2aaSAndroid Build Coastguard Worker         this->writeStageInputStruct();
1302*c8dee2aaSAndroid Build Coastguard Worker         this->writeStageOutputStruct();
1303*c8dee2aaSAndroid Build Coastguard Worker         this->writeUniformsAndBuffers();
1304*c8dee2aaSAndroid Build Coastguard Worker         this->writeNonBlockUniformsForTests();
1305*c8dee2aaSAndroid Build Coastguard Worker     }
1306*c8dee2aaSAndroid Build Coastguard Worker     StringStream body;
1307*c8dee2aaSAndroid Build Coastguard Worker     {
1308*c8dee2aaSAndroid Build Coastguard Worker         // Emit the program body.
1309*c8dee2aaSAndroid Build Coastguard Worker         AutoOutputStream outputToBody(this, &body, &fIndentation);
1310*c8dee2aaSAndroid Build Coastguard Worker         const FunctionDefinition* mainFunc = nullptr;
1311*c8dee2aaSAndroid Build Coastguard Worker         for (const ProgramElement* e : fProgram.elements()) {
1312*c8dee2aaSAndroid Build Coastguard Worker             this->writeProgramElement(*e);
1313*c8dee2aaSAndroid Build Coastguard Worker 
1314*c8dee2aaSAndroid Build Coastguard Worker             if (e->is<FunctionDefinition>()) {
1315*c8dee2aaSAndroid Build Coastguard Worker                 const FunctionDefinition& func = e->as<FunctionDefinition>();
1316*c8dee2aaSAndroid Build Coastguard Worker                 if (func.declaration().isMain()) {
1317*c8dee2aaSAndroid Build Coastguard Worker                     mainFunc = &func;
1318*c8dee2aaSAndroid Build Coastguard Worker                 }
1319*c8dee2aaSAndroid Build Coastguard Worker             }
1320*c8dee2aaSAndroid Build Coastguard Worker         }
1321*c8dee2aaSAndroid Build Coastguard Worker 
1322*c8dee2aaSAndroid Build Coastguard Worker         // At the bottom of the program body, emit the entrypoint function.
1323*c8dee2aaSAndroid Build Coastguard Worker         // The entrypoint relies on state that has been collected while we emitted the rest of the
1324*c8dee2aaSAndroid Build Coastguard Worker         // program, so it's important to do it last to make sure we don't miss anything.
1325*c8dee2aaSAndroid Build Coastguard Worker         if (mainFunc) {
1326*c8dee2aaSAndroid Build Coastguard Worker             this->writeEntryPoint(*mainFunc);
1327*c8dee2aaSAndroid Build Coastguard Worker         }
1328*c8dee2aaSAndroid Build Coastguard Worker     }
1329*c8dee2aaSAndroid Build Coastguard Worker 
1330*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(fHeader, *fOut);
1331*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(body, *fOut);
1332*c8dee2aaSAndroid Build Coastguard Worker 
1333*c8dee2aaSAndroid Build Coastguard Worker     this->writeUniformPolyfills();
1334*c8dee2aaSAndroid Build Coastguard Worker 
1335*c8dee2aaSAndroid Build Coastguard Worker     return fContext.fErrors->errorCount() == 0;
1336*c8dee2aaSAndroid Build Coastguard Worker }
1337*c8dee2aaSAndroid Build Coastguard Worker 
writeUniformPolyfills()1338*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeUniformPolyfills() {
1339*c8dee2aaSAndroid Build Coastguard Worker     // If we didn't encounter any uniforms that need polyfilling, there is nothing to do.
1340*c8dee2aaSAndroid Build Coastguard Worker     if (fFieldPolyfillMap.empty()) {
1341*c8dee2aaSAndroid Build Coastguard Worker         return;
1342*c8dee2aaSAndroid Build Coastguard Worker     }
1343*c8dee2aaSAndroid Build Coastguard Worker 
1344*c8dee2aaSAndroid Build Coastguard Worker     // We store the list of polyfilled fields as pointers in a hash-map, so the order can be
1345*c8dee2aaSAndroid Build Coastguard Worker     // inconsistent across runs. For determinism, we sort the polyfilled objects by name here.
1346*c8dee2aaSAndroid Build Coastguard Worker     TArray<const FieldPolyfillMap::Pair*> orderedFields;
1347*c8dee2aaSAndroid Build Coastguard Worker     orderedFields.reserve_exact(fFieldPolyfillMap.count());
1348*c8dee2aaSAndroid Build Coastguard Worker 
1349*c8dee2aaSAndroid Build Coastguard Worker     fFieldPolyfillMap.foreach([&](const FieldPolyfillMap::Pair& pair) {
1350*c8dee2aaSAndroid Build Coastguard Worker         orderedFields.push_back(&pair);
1351*c8dee2aaSAndroid Build Coastguard Worker     });
1352*c8dee2aaSAndroid Build Coastguard Worker 
1353*c8dee2aaSAndroid Build Coastguard Worker     std::sort(orderedFields.begin(),
1354*c8dee2aaSAndroid Build Coastguard Worker               orderedFields.end(),
1355*c8dee2aaSAndroid Build Coastguard Worker               [](const FieldPolyfillMap::Pair* a, const FieldPolyfillMap::Pair* b) {
1356*c8dee2aaSAndroid Build Coastguard Worker                   return a->second.fReplacementName < b->second.fReplacementName;
1357*c8dee2aaSAndroid Build Coastguard Worker               });
1358*c8dee2aaSAndroid Build Coastguard Worker 
1359*c8dee2aaSAndroid Build Coastguard Worker     THashSet<const Type*> writtenArrayElementPolyfill;
1360*c8dee2aaSAndroid Build Coastguard Worker     bool writtenUniformMatrixPolyfill[5][5] = {};  // m[column][row] for each matrix type
1361*c8dee2aaSAndroid Build Coastguard Worker     bool writtenUniformRowPolyfill[5] = {};        // for each matrix row-size
1362*c8dee2aaSAndroid Build Coastguard Worker     bool anyFieldAccessed = false;
1363*c8dee2aaSAndroid Build Coastguard Worker     for (const FieldPolyfillMap::Pair* pair : orderedFields) {
1364*c8dee2aaSAndroid Build Coastguard Worker         const auto& [field, info] = *pair;
1365*c8dee2aaSAndroid Build Coastguard Worker         const Type* fieldType = field->fType;
1366*c8dee2aaSAndroid Build Coastguard Worker         const Layout* fieldLayout = &field->fLayout;
1367*c8dee2aaSAndroid Build Coastguard Worker 
1368*c8dee2aaSAndroid Build Coastguard Worker         if (info.fIsArray) {
1369*c8dee2aaSAndroid Build Coastguard Worker             fieldType = &fieldType->componentType();
1370*c8dee2aaSAndroid Build Coastguard Worker             if (!writtenArrayElementPolyfill.contains(fieldType)) {
1371*c8dee2aaSAndroid Build Coastguard Worker                 writtenArrayElementPolyfill.add(fieldType);
1372*c8dee2aaSAndroid Build Coastguard Worker                 this->write("struct _skArrayElement_");
1373*c8dee2aaSAndroid Build Coastguard Worker                 this->write(fieldType->abbreviatedName());
1374*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(" {");
1375*c8dee2aaSAndroid Build Coastguard Worker 
1376*c8dee2aaSAndroid Build Coastguard Worker                 if (info.fIsMatrix) {
1377*c8dee2aaSAndroid Build Coastguard Worker                     // Create a struct representing the array containing std140-padded matrices.
1378*c8dee2aaSAndroid Build Coastguard Worker                     this->write("  e : _skMatrix");
1379*c8dee2aaSAndroid Build Coastguard Worker                     this->write(std::to_string(fieldType->columns()));
1380*c8dee2aaSAndroid Build Coastguard Worker                     this->writeLine(std::to_string(fieldType->rows()));
1381*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1382*c8dee2aaSAndroid Build Coastguard Worker                     // Create a struct representing the array with extra padding between elements.
1383*c8dee2aaSAndroid Build Coastguard Worker                     this->write("  @size(16) e : ");
1384*c8dee2aaSAndroid Build Coastguard Worker                     this->writeLine(to_wgsl_type(fContext, *fieldType, fieldLayout));
1385*c8dee2aaSAndroid Build Coastguard Worker                 }
1386*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine("};");
1387*c8dee2aaSAndroid Build Coastguard Worker             }
1388*c8dee2aaSAndroid Build Coastguard Worker         }
1389*c8dee2aaSAndroid Build Coastguard Worker 
1390*c8dee2aaSAndroid Build Coastguard Worker         if (info.fIsMatrix) {
1391*c8dee2aaSAndroid Build Coastguard Worker             // Create structs representing the matrix as an array of vectors, whether or not the
1392*c8dee2aaSAndroid Build Coastguard Worker             // matrix is ever accessed by the SkSL. (The struct itself is mentioned in the list of
1393*c8dee2aaSAndroid Build Coastguard Worker             // uniforms.)
1394*c8dee2aaSAndroid Build Coastguard Worker             int c = fieldType->columns();
1395*c8dee2aaSAndroid Build Coastguard Worker             int r = fieldType->rows();
1396*c8dee2aaSAndroid Build Coastguard Worker             if (!writtenUniformRowPolyfill[r]) {
1397*c8dee2aaSAndroid Build Coastguard Worker                 writtenUniformRowPolyfill[r] = true;
1398*c8dee2aaSAndroid Build Coastguard Worker 
1399*c8dee2aaSAndroid Build Coastguard Worker                 this->write("struct _skRow");
1400*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(r));
1401*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(" {");
1402*c8dee2aaSAndroid Build Coastguard Worker                 this->write("  @size(16) r : vec");
1403*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(r));
1404*c8dee2aaSAndroid Build Coastguard Worker                 this->write("<");
1405*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_wgsl_type(fContext, fieldType->componentType(), fieldLayout));
1406*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(">");
1407*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine("};");
1408*c8dee2aaSAndroid Build Coastguard Worker             }
1409*c8dee2aaSAndroid Build Coastguard Worker 
1410*c8dee2aaSAndroid Build Coastguard Worker             if (!writtenUniformMatrixPolyfill[c][r]) {
1411*c8dee2aaSAndroid Build Coastguard Worker                 writtenUniformMatrixPolyfill[c][r] = true;
1412*c8dee2aaSAndroid Build Coastguard Worker 
1413*c8dee2aaSAndroid Build Coastguard Worker                 this->write("struct _skMatrix");
1414*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(c));
1415*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(r));
1416*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(" {");
1417*c8dee2aaSAndroid Build Coastguard Worker                 this->write("  c : array<_skRow");
1418*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(r));
1419*c8dee2aaSAndroid Build Coastguard Worker                 this->write(", ");
1420*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(c));
1421*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(">");
1422*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine("};");
1423*c8dee2aaSAndroid Build Coastguard Worker             }
1424*c8dee2aaSAndroid Build Coastguard Worker         }
1425*c8dee2aaSAndroid Build Coastguard Worker 
1426*c8dee2aaSAndroid Build Coastguard Worker         // We create a polyfill variable only if the uniform was actually accessed.
1427*c8dee2aaSAndroid Build Coastguard Worker         if (!info.fWasAccessed) {
1428*c8dee2aaSAndroid Build Coastguard Worker             continue;
1429*c8dee2aaSAndroid Build Coastguard Worker         }
1430*c8dee2aaSAndroid Build Coastguard Worker         anyFieldAccessed = true;
1431*c8dee2aaSAndroid Build Coastguard Worker         this->write("var<private> ");
1432*c8dee2aaSAndroid Build Coastguard Worker         this->write(info.fReplacementName);
1433*c8dee2aaSAndroid Build Coastguard Worker         this->write(": ");
1434*c8dee2aaSAndroid Build Coastguard Worker 
1435*c8dee2aaSAndroid Build Coastguard Worker         const Type& interfaceBlockType = info.fInterfaceBlock->var()->type();
1436*c8dee2aaSAndroid Build Coastguard Worker         if (interfaceBlockType.isArray()) {
1437*c8dee2aaSAndroid Build Coastguard Worker             this->write("array<");
1438*c8dee2aaSAndroid Build Coastguard Worker             this->write(to_wgsl_type(fContext, *field->fType, fieldLayout));
1439*c8dee2aaSAndroid Build Coastguard Worker             this->write(", ");
1440*c8dee2aaSAndroid Build Coastguard Worker             this->write(std::to_string(interfaceBlockType.columns()));
1441*c8dee2aaSAndroid Build Coastguard Worker             this->write(">");
1442*c8dee2aaSAndroid Build Coastguard Worker         } else {
1443*c8dee2aaSAndroid Build Coastguard Worker             this->write(to_wgsl_type(fContext, *field->fType, fieldLayout));
1444*c8dee2aaSAndroid Build Coastguard Worker         }
1445*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
1446*c8dee2aaSAndroid Build Coastguard Worker     }
1447*c8dee2aaSAndroid Build Coastguard Worker 
1448*c8dee2aaSAndroid Build Coastguard Worker     // If no fields were actually accessed, _skInitializePolyfilledUniforms will not be called and
1449*c8dee2aaSAndroid Build Coastguard Worker     // we can avoid emitting an empty, dead function.
1450*c8dee2aaSAndroid Build Coastguard Worker     if (!anyFieldAccessed) {
1451*c8dee2aaSAndroid Build Coastguard Worker         return;
1452*c8dee2aaSAndroid Build Coastguard Worker     }
1453*c8dee2aaSAndroid Build Coastguard Worker 
1454*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("fn _skInitializePolyfilledUniforms() {");
1455*c8dee2aaSAndroid Build Coastguard Worker     ++fIndentation;
1456*c8dee2aaSAndroid Build Coastguard Worker 
1457*c8dee2aaSAndroid Build Coastguard Worker     for (const FieldPolyfillMap::Pair* pair : orderedFields) {
1458*c8dee2aaSAndroid Build Coastguard Worker         // Only initialize a polyfill global if the uniform was actually accessed.
1459*c8dee2aaSAndroid Build Coastguard Worker         const auto& [field, info] = *pair;
1460*c8dee2aaSAndroid Build Coastguard Worker         if (!info.fWasAccessed) {
1461*c8dee2aaSAndroid Build Coastguard Worker             continue;
1462*c8dee2aaSAndroid Build Coastguard Worker         }
1463*c8dee2aaSAndroid Build Coastguard Worker 
1464*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize the name of this uniform variable
1465*c8dee2aaSAndroid Build Coastguard Worker         std::string_view instanceName = info.fInterfaceBlock->instanceName();
1466*c8dee2aaSAndroid Build Coastguard Worker         const Type& interfaceBlockType = info.fInterfaceBlock->var()->type();
1467*c8dee2aaSAndroid Build Coastguard Worker         if (instanceName.empty()) {
1468*c8dee2aaSAndroid Build Coastguard Worker             instanceName = fInterfaceBlockNameMap[&interfaceBlockType.componentType()];
1469*c8dee2aaSAndroid Build Coastguard Worker         }
1470*c8dee2aaSAndroid Build Coastguard Worker 
1471*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the global variable associated with this uniform.
1472*c8dee2aaSAndroid Build Coastguard Worker         // If the interface block is arrayed, the associated global will be arrayed as well.
1473*c8dee2aaSAndroid Build Coastguard Worker         int numIBElements = interfaceBlockType.isArray() ? interfaceBlockType.columns() : 1;
1474*c8dee2aaSAndroid Build Coastguard Worker         for (int ibIdx = 0; ibIdx < numIBElements; ++ibIdx) {
1475*c8dee2aaSAndroid Build Coastguard Worker             this->write(info.fReplacementName);
1476*c8dee2aaSAndroid Build Coastguard Worker             if (interfaceBlockType.isArray()) {
1477*c8dee2aaSAndroid Build Coastguard Worker                 this->write("[");
1478*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(ibIdx));
1479*c8dee2aaSAndroid Build Coastguard Worker                 this->write("]");
1480*c8dee2aaSAndroid Build Coastguard Worker             }
1481*c8dee2aaSAndroid Build Coastguard Worker             this->write(" = ");
1482*c8dee2aaSAndroid Build Coastguard Worker 
1483*c8dee2aaSAndroid Build Coastguard Worker             const Type* fieldType = field->fType;
1484*c8dee2aaSAndroid Build Coastguard Worker             const Layout* fieldLayout = &field->fLayout;
1485*c8dee2aaSAndroid Build Coastguard Worker 
1486*c8dee2aaSAndroid Build Coastguard Worker             int numArrayElements;
1487*c8dee2aaSAndroid Build Coastguard Worker             if (info.fIsArray) {
1488*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_wgsl_type(fContext, *fieldType, fieldLayout));
1489*c8dee2aaSAndroid Build Coastguard Worker                 this->write("(");
1490*c8dee2aaSAndroid Build Coastguard Worker                 numArrayElements = fieldType->columns();
1491*c8dee2aaSAndroid Build Coastguard Worker                 fieldType = &fieldType->componentType();
1492*c8dee2aaSAndroid Build Coastguard Worker             } else {
1493*c8dee2aaSAndroid Build Coastguard Worker                 numArrayElements = 1;
1494*c8dee2aaSAndroid Build Coastguard Worker             }
1495*c8dee2aaSAndroid Build Coastguard Worker 
1496*c8dee2aaSAndroid Build Coastguard Worker             auto arraySeparator = String::Separator();
1497*c8dee2aaSAndroid Build Coastguard Worker             for (int arrayIdx = 0; arrayIdx < numArrayElements; arrayIdx++) {
1498*c8dee2aaSAndroid Build Coastguard Worker                 this->write(arraySeparator());
1499*c8dee2aaSAndroid Build Coastguard Worker 
1500*c8dee2aaSAndroid Build Coastguard Worker                 std::string fieldName{instanceName};
1501*c8dee2aaSAndroid Build Coastguard Worker                 if (interfaceBlockType.isArray()) {
1502*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += '[';
1503*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += std::to_string(ibIdx);
1504*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += ']';
1505*c8dee2aaSAndroid Build Coastguard Worker                 }
1506*c8dee2aaSAndroid Build Coastguard Worker                 fieldName += '.';
1507*c8dee2aaSAndroid Build Coastguard Worker                 fieldName += this->assembleName(field->fName);
1508*c8dee2aaSAndroid Build Coastguard Worker 
1509*c8dee2aaSAndroid Build Coastguard Worker                 if (info.fIsArray) {
1510*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += '[';
1511*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += std::to_string(arrayIdx);
1512*c8dee2aaSAndroid Build Coastguard Worker                     fieldName += "].e";
1513*c8dee2aaSAndroid Build Coastguard Worker                 }
1514*c8dee2aaSAndroid Build Coastguard Worker 
1515*c8dee2aaSAndroid Build Coastguard Worker                 if (info.fIsMatrix) {
1516*c8dee2aaSAndroid Build Coastguard Worker                     this->write(to_wgsl_type(fContext, *fieldType, fieldLayout));
1517*c8dee2aaSAndroid Build Coastguard Worker                     this->write("(");
1518*c8dee2aaSAndroid Build Coastguard Worker                     int numColumns = fieldType->columns();
1519*c8dee2aaSAndroid Build Coastguard Worker                     auto matrixSeparator = String::Separator();
1520*c8dee2aaSAndroid Build Coastguard Worker                     for (int column = 0; column < numColumns; column++) {
1521*c8dee2aaSAndroid Build Coastguard Worker                         this->write(matrixSeparator());
1522*c8dee2aaSAndroid Build Coastguard Worker                         this->write(fieldName);
1523*c8dee2aaSAndroid Build Coastguard Worker                         this->write(".c[");
1524*c8dee2aaSAndroid Build Coastguard Worker                         this->write(std::to_string(column));
1525*c8dee2aaSAndroid Build Coastguard Worker                         this->write("].r");
1526*c8dee2aaSAndroid Build Coastguard Worker                     }
1527*c8dee2aaSAndroid Build Coastguard Worker                     this->write(")");
1528*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1529*c8dee2aaSAndroid Build Coastguard Worker                     this->write(fieldName);
1530*c8dee2aaSAndroid Build Coastguard Worker                 }
1531*c8dee2aaSAndroid Build Coastguard Worker             }
1532*c8dee2aaSAndroid Build Coastguard Worker 
1533*c8dee2aaSAndroid Build Coastguard Worker             if (info.fIsArray) {
1534*c8dee2aaSAndroid Build Coastguard Worker                 this->write(")");
1535*c8dee2aaSAndroid Build Coastguard Worker             }
1536*c8dee2aaSAndroid Build Coastguard Worker 
1537*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(";");
1538*c8dee2aaSAndroid Build Coastguard Worker         }
1539*c8dee2aaSAndroid Build Coastguard Worker     }
1540*c8dee2aaSAndroid Build Coastguard Worker 
1541*c8dee2aaSAndroid Build Coastguard Worker     --fIndentation;
1542*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
1543*c8dee2aaSAndroid Build Coastguard Worker }
1544*c8dee2aaSAndroid Build Coastguard Worker 
1545*c8dee2aaSAndroid Build Coastguard Worker 
preprocessProgram()1546*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::preprocessProgram() {
1547*c8dee2aaSAndroid Build Coastguard Worker     fRequirements = resolve_program_requirements(&fProgram);
1548*c8dee2aaSAndroid Build Coastguard Worker     collect_pipeline_io_vars(&fProgram, &fPipelineInputs, ModifierFlag::kIn);
1549*c8dee2aaSAndroid Build Coastguard Worker     collect_pipeline_io_vars(&fProgram, &fPipelineOutputs, ModifierFlag::kOut);
1550*c8dee2aaSAndroid Build Coastguard Worker }
1551*c8dee2aaSAndroid Build Coastguard Worker 
write(std::string_view s)1552*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::write(std::string_view s) {
1553*c8dee2aaSAndroid Build Coastguard Worker     if (s.empty()) {
1554*c8dee2aaSAndroid Build Coastguard Worker         return;
1555*c8dee2aaSAndroid Build Coastguard Worker     }
1556*c8dee2aaSAndroid Build Coastguard Worker     if (fAtLineStart && fPrettyPrint == PrettyPrint::kYes) {
1557*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fIndentation; i++) {
1558*c8dee2aaSAndroid Build Coastguard Worker             fOut->writeText("  ");
1559*c8dee2aaSAndroid Build Coastguard Worker         }
1560*c8dee2aaSAndroid Build Coastguard Worker     }
1561*c8dee2aaSAndroid Build Coastguard Worker     fOut->writeText(std::string(s).c_str());
1562*c8dee2aaSAndroid Build Coastguard Worker     fAtLineStart = false;
1563*c8dee2aaSAndroid Build Coastguard Worker }
1564*c8dee2aaSAndroid Build Coastguard Worker 
writeLine(std::string_view s)1565*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeLine(std::string_view s) {
1566*c8dee2aaSAndroid Build Coastguard Worker     this->write(s);
1567*c8dee2aaSAndroid Build Coastguard Worker     fOut->writeText("\n");
1568*c8dee2aaSAndroid Build Coastguard Worker     fAtLineStart = true;
1569*c8dee2aaSAndroid Build Coastguard Worker }
1570*c8dee2aaSAndroid Build Coastguard Worker 
finishLine()1571*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::finishLine() {
1572*c8dee2aaSAndroid Build Coastguard Worker     if (!fAtLineStart) {
1573*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine();
1574*c8dee2aaSAndroid Build Coastguard Worker     }
1575*c8dee2aaSAndroid Build Coastguard Worker }
1576*c8dee2aaSAndroid Build Coastguard Worker 
assembleName(std::string_view name)1577*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleName(std::string_view name) {
1578*c8dee2aaSAndroid Build Coastguard Worker     if (name.empty()) {
1579*c8dee2aaSAndroid Build Coastguard Worker         // WGSL doesn't allow anonymous function parameters.
1580*c8dee2aaSAndroid Build Coastguard Worker         return "_skAnonymous" + std::to_string(fScratchCount++);
1581*c8dee2aaSAndroid Build Coastguard Worker     }
1582*c8dee2aaSAndroid Build Coastguard Worker     // Add `R_` before reserved names to avoid any potential reserved-word conflict.
1583*c8dee2aaSAndroid Build Coastguard Worker     return (skstd::starts_with(name, "_sk") ||
1584*c8dee2aaSAndroid Build Coastguard Worker             skstd::starts_with(name, "R_") ||
1585*c8dee2aaSAndroid Build Coastguard Worker             is_reserved_word(name))
1586*c8dee2aaSAndroid Build Coastguard Worker                    ? std::string("R_") + std::string(name)
1587*c8dee2aaSAndroid Build Coastguard Worker                    : std::string(name);
1588*c8dee2aaSAndroid Build Coastguard Worker }
1589*c8dee2aaSAndroid Build Coastguard Worker 
writeVariableDecl(const Layout & layout,const Type & type,std::string_view name,Delimiter delimiter)1590*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeVariableDecl(const Layout& layout,
1591*c8dee2aaSAndroid Build Coastguard Worker                                           const Type& type,
1592*c8dee2aaSAndroid Build Coastguard Worker                                           std::string_view name,
1593*c8dee2aaSAndroid Build Coastguard Worker                                           Delimiter delimiter) {
1594*c8dee2aaSAndroid Build Coastguard Worker     this->write(this->assembleName(name));
1595*c8dee2aaSAndroid Build Coastguard Worker     this->write(": " + to_wgsl_type(fContext, type, &layout));
1596*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(delimiter_to_str(delimiter));
1597*c8dee2aaSAndroid Build Coastguard Worker }
1598*c8dee2aaSAndroid Build Coastguard Worker 
writePipelineIODeclaration(const Layout & layout,const Type & type,ModifierFlags modifiers,std::string_view name,Delimiter delimiter)1599*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writePipelineIODeclaration(const Layout& layout,
1600*c8dee2aaSAndroid Build Coastguard Worker                                                    const Type& type,
1601*c8dee2aaSAndroid Build Coastguard Worker                                                    ModifierFlags modifiers,
1602*c8dee2aaSAndroid Build Coastguard Worker                                                    std::string_view name,
1603*c8dee2aaSAndroid Build Coastguard Worker                                                    Delimiter delimiter) {
1604*c8dee2aaSAndroid Build Coastguard Worker     // In WGSL, an entry-point IO parameter is "one of either a built-in value or assigned a
1605*c8dee2aaSAndroid Build Coastguard Worker     // location". However, some SkSL declarations, specifically sk_FragColor, can contain both a
1606*c8dee2aaSAndroid Build Coastguard Worker     // location and a builtin modifier. In addition, WGSL doesn't have a built-in equivalent for
1607*c8dee2aaSAndroid Build Coastguard Worker     // sk_FragColor as it relies on the user-defined location for a render target.
1608*c8dee2aaSAndroid Build Coastguard Worker     //
1609*c8dee2aaSAndroid Build Coastguard Worker     // Instead of special-casing sk_FragColor, we just give higher precedence to a location modifier
1610*c8dee2aaSAndroid Build Coastguard Worker     // if a declaration happens to both have a location and it's a built-in.
1611*c8dee2aaSAndroid Build Coastguard Worker     //
1612*c8dee2aaSAndroid Build Coastguard Worker     // Also see:
1613*c8dee2aaSAndroid Build Coastguard Worker     // https://www.w3.org/TR/WGSL/#input-output-locations
1614*c8dee2aaSAndroid Build Coastguard Worker     // https://www.w3.org/TR/WGSL/#attribute-location
1615*c8dee2aaSAndroid Build Coastguard Worker     // https://www.w3.org/TR/WGSL/#builtin-inputs-outputs
1616*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fLocation >= 0) {
1617*c8dee2aaSAndroid Build Coastguard Worker         this->writeUserDefinedIODecl(layout, type, modifiers, name, delimiter);
1618*c8dee2aaSAndroid Build Coastguard Worker         return;
1619*c8dee2aaSAndroid Build Coastguard Worker     }
1620*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fBuiltin >= 0) {
1621*c8dee2aaSAndroid Build Coastguard Worker         if (layout.fBuiltin == SK_POINTSIZE_BUILTIN) {
1622*c8dee2aaSAndroid Build Coastguard Worker             // WebGPU does not support the point-size builtin, but we silently replace it with a
1623*c8dee2aaSAndroid Build Coastguard Worker             // global variable when it is used, instead of reporting an error.
1624*c8dee2aaSAndroid Build Coastguard Worker             return;
1625*c8dee2aaSAndroid Build Coastguard Worker         }
1626*c8dee2aaSAndroid Build Coastguard Worker         auto builtin = builtin_from_sksl_name(layout.fBuiltin);
1627*c8dee2aaSAndroid Build Coastguard Worker         if (builtin.has_value()) {
1628*c8dee2aaSAndroid Build Coastguard Worker             // Builtin IO parameters should only have in/out modifiers, which are then implicit in
1629*c8dee2aaSAndroid Build Coastguard Worker             // the generated WGSL, hence why writeBuiltinIODecl does not need them passed in.
1630*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!(modifiers & ~(ModifierFlag::kIn | ModifierFlag::kOut)));
1631*c8dee2aaSAndroid Build Coastguard Worker             this->writeBuiltinIODecl(type, name, *builtin, delimiter);
1632*c8dee2aaSAndroid Build Coastguard Worker             return;
1633*c8dee2aaSAndroid Build Coastguard Worker         }
1634*c8dee2aaSAndroid Build Coastguard Worker     }
1635*c8dee2aaSAndroid Build Coastguard Worker     fContext.fErrors->error(Position(), "declaration '" + std::string(name) + "' is not supported");
1636*c8dee2aaSAndroid Build Coastguard Worker }
1637*c8dee2aaSAndroid Build Coastguard Worker 
writeUserDefinedIODecl(const Layout & layout,const Type & type,ModifierFlags flags,std::string_view name,Delimiter delimiter)1638*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeUserDefinedIODecl(const Layout& layout,
1639*c8dee2aaSAndroid Build Coastguard Worker                                                const Type& type,
1640*c8dee2aaSAndroid Build Coastguard Worker                                                ModifierFlags flags,
1641*c8dee2aaSAndroid Build Coastguard Worker                                                std::string_view name,
1642*c8dee2aaSAndroid Build Coastguard Worker                                                Delimiter delimiter) {
1643*c8dee2aaSAndroid Build Coastguard Worker     this->write("@location(" + std::to_string(layout.fLocation) + ") ");
1644*c8dee2aaSAndroid Build Coastguard Worker 
1645*c8dee2aaSAndroid Build Coastguard Worker     // @blend_src is only allowed when doing dual-source blending, and only on color attachment 0.
1646*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fLocation == 0 && layout.fIndex >= 0 && fProgram.fInterface.fOutputSecondaryColor) {
1647*c8dee2aaSAndroid Build Coastguard Worker         this->write("@blend_src(" + std::to_string(layout.fIndex) + ") ");
1648*c8dee2aaSAndroid Build Coastguard Worker     }
1649*c8dee2aaSAndroid Build Coastguard Worker 
1650*c8dee2aaSAndroid Build Coastguard Worker     // "User-defined IO of scalar or vector integer type must always be specified as
1651*c8dee2aaSAndroid Build Coastguard Worker     // @interpolate(flat)" (see https://www.w3.org/TR/WGSL/#interpolation)
1652*c8dee2aaSAndroid Build Coastguard Worker     if (flags.isFlat() || type.isInteger() ||
1653*c8dee2aaSAndroid Build Coastguard Worker         (type.isVector() && type.componentType().isInteger())) {
1654*c8dee2aaSAndroid Build Coastguard Worker         // We can use 'either' to hint to WebGPU that we don't care about the provoking vertex and
1655*c8dee2aaSAndroid Build Coastguard Worker         // avoid any expensive shader or data rewriting to ensure 'first'. Skia has a long-standing
1656*c8dee2aaSAndroid Build Coastguard Worker         // policy to only use flat shading when it's constant for a primitive so the vertex doesn't
1657*c8dee2aaSAndroid Build Coastguard Worker         // matter. See https://www.w3.org/TR/WGSL/#interpolation-sampling-either
1658*c8dee2aaSAndroid Build Coastguard Worker         this->write("@interpolate(flat, either) ");
1659*c8dee2aaSAndroid Build Coastguard Worker     } else if (flags & ModifierFlag::kNoPerspective) {
1660*c8dee2aaSAndroid Build Coastguard Worker         this->write("@interpolate(linear) ");
1661*c8dee2aaSAndroid Build Coastguard Worker     }
1662*c8dee2aaSAndroid Build Coastguard Worker 
1663*c8dee2aaSAndroid Build Coastguard Worker     this->writeVariableDecl(layout, type, name, delimiter);
1664*c8dee2aaSAndroid Build Coastguard Worker }
1665*c8dee2aaSAndroid Build Coastguard Worker 
writeBuiltinIODecl(const Type & type,std::string_view name,Builtin builtin,Delimiter delimiter)1666*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeBuiltinIODecl(const Type& type,
1667*c8dee2aaSAndroid Build Coastguard Worker                                            std::string_view name,
1668*c8dee2aaSAndroid Build Coastguard Worker                                            Builtin builtin,
1669*c8dee2aaSAndroid Build Coastguard Worker                                            Delimiter delimiter) {
1670*c8dee2aaSAndroid Build Coastguard Worker     this->write(wgsl_builtin_name(builtin));
1671*c8dee2aaSAndroid Build Coastguard Worker     this->write(" ");
1672*c8dee2aaSAndroid Build Coastguard Worker     this->write(this->assembleName(name));
1673*c8dee2aaSAndroid Build Coastguard Worker     this->write(": ");
1674*c8dee2aaSAndroid Build Coastguard Worker     this->write(wgsl_builtin_type(builtin));
1675*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(delimiter_to_str(delimiter));
1676*c8dee2aaSAndroid Build Coastguard Worker }
1677*c8dee2aaSAndroid Build Coastguard Worker 
writeFunction(const FunctionDefinition & f)1678*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
1679*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration& decl = f.declaration();
1680*c8dee2aaSAndroid Build Coastguard Worker     fHasUnconditionalReturn = false;
1681*c8dee2aaSAndroid Build Coastguard Worker     fConditionalScopeDepth = 0;
1682*c8dee2aaSAndroid Build Coastguard Worker 
1683*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fAtFunctionScope);
1684*c8dee2aaSAndroid Build Coastguard Worker     fAtFunctionScope = true;
1685*c8dee2aaSAndroid Build Coastguard Worker 
1686*c8dee2aaSAndroid Build Coastguard Worker     // WGSL parameters are immutable and are considered as taking no storage, but SkSL parameters
1687*c8dee2aaSAndroid Build Coastguard Worker     // are real variables. To work around this, we make var-based copies of parameters. It's
1688*c8dee2aaSAndroid Build Coastguard Worker     // wasteful to make a copy of every single parameter--even if the compiler can eventually
1689*c8dee2aaSAndroid Build Coastguard Worker     // optimize them all away, that takes time and generates bloated code. So, we only make
1690*c8dee2aaSAndroid Build Coastguard Worker     // parameter copies if the variable is actually written-to.
1691*c8dee2aaSAndroid Build Coastguard Worker     STArray<32, bool> paramNeedsDedicatedStorage;
1692*c8dee2aaSAndroid Build Coastguard Worker     paramNeedsDedicatedStorage.push_back_n(decl.parameters().size(), true);
1693*c8dee2aaSAndroid Build Coastguard Worker 
1694*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < decl.parameters().size(); ++index) {
1695*c8dee2aaSAndroid Build Coastguard Worker         const Variable& param = *decl.parameters()[index];
1696*c8dee2aaSAndroid Build Coastguard Worker         if (param.type().isOpaque() || param.name().empty()) {
1697*c8dee2aaSAndroid Build Coastguard Worker             // Opaque-typed or anonymous parameters don't need dedicated storage.
1698*c8dee2aaSAndroid Build Coastguard Worker             paramNeedsDedicatedStorage[index] = false;
1699*c8dee2aaSAndroid Build Coastguard Worker             continue;
1700*c8dee2aaSAndroid Build Coastguard Worker         }
1701*c8dee2aaSAndroid Build Coastguard Worker 
1702*c8dee2aaSAndroid Build Coastguard Worker         const ProgramUsage::VariableCounts counts = fProgram.fUsage->get(param);
1703*c8dee2aaSAndroid Build Coastguard Worker         if ((param.modifierFlags() & ModifierFlag::kOut) || counts.fWrite == 0) {
1704*c8dee2aaSAndroid Build Coastguard Worker             // Variables which are never written-to don't need dedicated storage.
1705*c8dee2aaSAndroid Build Coastguard Worker             // Out-parameters are passed as pointers; the pointer itself is never modified, so
1706*c8dee2aaSAndroid Build Coastguard Worker             // it doesn't need dedicated storage.
1707*c8dee2aaSAndroid Build Coastguard Worker             paramNeedsDedicatedStorage[index] = false;
1708*c8dee2aaSAndroid Build Coastguard Worker         }
1709*c8dee2aaSAndroid Build Coastguard Worker     }
1710*c8dee2aaSAndroid Build Coastguard Worker 
1711*c8dee2aaSAndroid Build Coastguard Worker     this->writeFunctionDeclaration(decl, paramNeedsDedicatedStorage);
1712*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(" {");
1713*c8dee2aaSAndroid Build Coastguard Worker     ++fIndentation;
1714*c8dee2aaSAndroid Build Coastguard Worker 
1715*c8dee2aaSAndroid Build Coastguard Worker     // The parameters were given generic names like `_skParam1`, because WGSL parameters don't have
1716*c8dee2aaSAndroid Build Coastguard Worker     // storage and are immutable. If mutability is required, we create variables here; otherwise, we
1717*c8dee2aaSAndroid Build Coastguard Worker     // create properly-named `let` aliases.
1718*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < decl.parameters().size(); ++index) {
1719*c8dee2aaSAndroid Build Coastguard Worker         if (paramNeedsDedicatedStorage[index]) {
1720*c8dee2aaSAndroid Build Coastguard Worker             const Variable& param = *decl.parameters()[index];
1721*c8dee2aaSAndroid Build Coastguard Worker             this->write("var ");
1722*c8dee2aaSAndroid Build Coastguard Worker             this->write(this->assembleName(param.mangledName()));
1723*c8dee2aaSAndroid Build Coastguard Worker             this->write(" = _skParam");
1724*c8dee2aaSAndroid Build Coastguard Worker             this->write(std::to_string(index));
1725*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(";");
1726*c8dee2aaSAndroid Build Coastguard Worker         }
1727*c8dee2aaSAndroid Build Coastguard Worker     }
1728*c8dee2aaSAndroid Build Coastguard Worker 
1729*c8dee2aaSAndroid Build Coastguard Worker     this->writeBlock(f.body()->as<Block>());
1730*c8dee2aaSAndroid Build Coastguard Worker 
1731*c8dee2aaSAndroid Build Coastguard Worker     // If fConditionalScopeDepth isn't zero, we have an unbalanced +1 or -1 when updating the depth.
1732*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fConditionalScopeDepth == 0);
1733*c8dee2aaSAndroid Build Coastguard Worker     if (!fHasUnconditionalReturn && !f.declaration().returnType().isVoid()) {
1734*c8dee2aaSAndroid Build Coastguard Worker         this->write("return ");
1735*c8dee2aaSAndroid Build Coastguard Worker         this->write(to_wgsl_type(fContext, f.declaration().returnType()));
1736*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("();");
1737*c8dee2aaSAndroid Build Coastguard Worker     }
1738*c8dee2aaSAndroid Build Coastguard Worker 
1739*c8dee2aaSAndroid Build Coastguard Worker     --fIndentation;
1740*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
1741*c8dee2aaSAndroid Build Coastguard Worker 
1742*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fAtFunctionScope);
1743*c8dee2aaSAndroid Build Coastguard Worker     fAtFunctionScope = false;
1744*c8dee2aaSAndroid Build Coastguard Worker }
1745*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionDeclaration(const FunctionDeclaration & decl,SkSpan<const bool> paramNeedsDedicatedStorage)1746*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& decl,
1747*c8dee2aaSAndroid Build Coastguard Worker                                                  SkSpan<const bool> paramNeedsDedicatedStorage) {
1748*c8dee2aaSAndroid Build Coastguard Worker     this->write("fn ");
1749*c8dee2aaSAndroid Build Coastguard Worker     if (decl.isMain()) {
1750*c8dee2aaSAndroid Build Coastguard Worker         this->write("_skslMain(");
1751*c8dee2aaSAndroid Build Coastguard Worker     } else {
1752*c8dee2aaSAndroid Build Coastguard Worker         this->write(this->assembleName(decl.mangledName()));
1753*c8dee2aaSAndroid Build Coastguard Worker         this->write("(");
1754*c8dee2aaSAndroid Build Coastguard Worker     }
1755*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
1756*c8dee2aaSAndroid Build Coastguard Worker     if (this->writeFunctionDependencyParams(decl)) {
1757*c8dee2aaSAndroid Build Coastguard Worker         separator();  // update the separator as parameters have been written
1758*c8dee2aaSAndroid Build Coastguard Worker     }
1759*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < decl.parameters().size(); ++index) {
1760*c8dee2aaSAndroid Build Coastguard Worker         this->write(separator());
1761*c8dee2aaSAndroid Build Coastguard Worker 
1762*c8dee2aaSAndroid Build Coastguard Worker         const Variable& param = *decl.parameters()[index];
1763*c8dee2aaSAndroid Build Coastguard Worker         if (param.type().isOpaque()) {
1764*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!paramNeedsDedicatedStorage[index]);
1765*c8dee2aaSAndroid Build Coastguard Worker             if (param.type().isSampler()) {
1766*c8dee2aaSAndroid Build Coastguard Worker                 // Create parameters for both the texture and associated sampler.
1767*c8dee2aaSAndroid Build Coastguard Worker                 this->write(param.name());
1768*c8dee2aaSAndroid Build Coastguard Worker                 this->write(kTextureSuffix);
1769*c8dee2aaSAndroid Build Coastguard Worker                 this->write(": texture_2d<f32>, ");
1770*c8dee2aaSAndroid Build Coastguard Worker                 this->write(param.name());
1771*c8dee2aaSAndroid Build Coastguard Worker                 this->write(kSamplerSuffix);
1772*c8dee2aaSAndroid Build Coastguard Worker                 this->write(": sampler");
1773*c8dee2aaSAndroid Build Coastguard Worker             } else {
1774*c8dee2aaSAndroid Build Coastguard Worker                 // Create a parameter for the opaque object.
1775*c8dee2aaSAndroid Build Coastguard Worker                 this->write(param.name());
1776*c8dee2aaSAndroid Build Coastguard Worker                 this->write(": ");
1777*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_wgsl_type(fContext, param.type(), &param.layout()));
1778*c8dee2aaSAndroid Build Coastguard Worker             }
1779*c8dee2aaSAndroid Build Coastguard Worker         } else {
1780*c8dee2aaSAndroid Build Coastguard Worker             if (paramNeedsDedicatedStorage[index] || param.name().empty()) {
1781*c8dee2aaSAndroid Build Coastguard Worker                 // Create an unnamed parameter. If the parameter needs dedicated storage, it will
1782*c8dee2aaSAndroid Build Coastguard Worker                 // later be assigned a `var` in the function body. (If it's anonymous, a var isn't
1783*c8dee2aaSAndroid Build Coastguard Worker                 // needed.)
1784*c8dee2aaSAndroid Build Coastguard Worker                 this->write("_skParam");
1785*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(index));
1786*c8dee2aaSAndroid Build Coastguard Worker             } else {
1787*c8dee2aaSAndroid Build Coastguard Worker                 // Use the name directly from the SkSL program.
1788*c8dee2aaSAndroid Build Coastguard Worker                 this->write(this->assembleName(param.name()));
1789*c8dee2aaSAndroid Build Coastguard Worker             }
1790*c8dee2aaSAndroid Build Coastguard Worker             this->write(": ");
1791*c8dee2aaSAndroid Build Coastguard Worker             if (param.type().isUnsizedArray()) {
1792*c8dee2aaSAndroid Build Coastguard Worker                 // Creates a storage address space pointer for unsized array parameters.
1793*c8dee2aaSAndroid Build Coastguard Worker                 // The buffer the array resides in must be marked `readonly` to have the array
1794*c8dee2aaSAndroid Build Coastguard Worker                 // be used in function parameters, since access modes in wgsl must exactly match.
1795*c8dee2aaSAndroid Build Coastguard Worker                 this->write("ptr<storage, ");
1796*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_wgsl_type(fContext, param.type(), &param.layout()));
1797*c8dee2aaSAndroid Build Coastguard Worker                 this->write(", read>");
1798*c8dee2aaSAndroid Build Coastguard Worker             } else if (param.modifierFlags() & ModifierFlag::kOut) {
1799*c8dee2aaSAndroid Build Coastguard Worker                 // Declare an "out" function parameter as a pointer.
1800*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_ptr_type(fContext, param.type(), &param.layout()));
1801*c8dee2aaSAndroid Build Coastguard Worker             } else {
1802*c8dee2aaSAndroid Build Coastguard Worker                 this->write(to_wgsl_type(fContext, param.type(), &param.layout()));
1803*c8dee2aaSAndroid Build Coastguard Worker             }
1804*c8dee2aaSAndroid Build Coastguard Worker         }
1805*c8dee2aaSAndroid Build Coastguard Worker     }
1806*c8dee2aaSAndroid Build Coastguard Worker     this->write(")");
1807*c8dee2aaSAndroid Build Coastguard Worker     if (!decl.returnType().isVoid()) {
1808*c8dee2aaSAndroid Build Coastguard Worker         this->write(" -> ");
1809*c8dee2aaSAndroid Build Coastguard Worker         this->write(to_wgsl_type(fContext, decl.returnType()));
1810*c8dee2aaSAndroid Build Coastguard Worker     }
1811*c8dee2aaSAndroid Build Coastguard Worker }
1812*c8dee2aaSAndroid Build Coastguard Worker 
writeEntryPoint(const FunctionDefinition & main)1813*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeEntryPoint(const FunctionDefinition& main) {
1814*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(main.declaration().isMain());
1815*c8dee2aaSAndroid Build Coastguard Worker     const ProgramKind programKind = fProgram.fConfig->fKind;
1816*c8dee2aaSAndroid Build Coastguard Worker 
1817*c8dee2aaSAndroid Build Coastguard Worker     if (fGenSyntheticCode == IncludeSyntheticCode::kYes &&
1818*c8dee2aaSAndroid Build Coastguard Worker         ProgramConfig::IsRuntimeShader(programKind)) {
1819*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize a basic entrypoint which just calls straight through to main.
1820*c8dee2aaSAndroid Build Coastguard Worker         // This is only used by skslc and just needs to pass the WGSL validator; Skia won't ever
1821*c8dee2aaSAndroid Build Coastguard Worker         // emit functions like this.
1822*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("@fragment fn main(@location(0) _coords: vec2<f32>) -> "
1823*c8dee2aaSAndroid Build Coastguard Worker                                      "@location(0) vec4<f32> {");
1824*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
1825*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("return _skslMain(_coords);");
1826*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
1827*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
1828*c8dee2aaSAndroid Build Coastguard Worker         return;
1829*c8dee2aaSAndroid Build Coastguard Worker     }
1830*c8dee2aaSAndroid Build Coastguard Worker 
1831*c8dee2aaSAndroid Build Coastguard Worker     // The input and output parameters for a vertex/fragment stage entry point function have the
1832*c8dee2aaSAndroid Build Coastguard Worker     // FSIn/FSOut/VSIn/VSOut/CSIn struct types that have been synthesized in generateCode(). An
1833*c8dee2aaSAndroid Build Coastguard Worker     // entrypoint always has a predictable signature and acts as a trampoline to the user-defined
1834*c8dee2aaSAndroid Build Coastguard Worker     // main function.
1835*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsVertex(programKind)) {
1836*c8dee2aaSAndroid Build Coastguard Worker         this->write("@vertex");
1837*c8dee2aaSAndroid Build Coastguard Worker     } else if (ProgramConfig::IsFragment(programKind)) {
1838*c8dee2aaSAndroid Build Coastguard Worker         this->write("@fragment");
1839*c8dee2aaSAndroid Build Coastguard Worker     } else if (ProgramConfig::IsCompute(programKind)) {
1840*c8dee2aaSAndroid Build Coastguard Worker         this->write("@compute @workgroup_size(");
1841*c8dee2aaSAndroid Build Coastguard Worker         this->write(std::to_string(fLocalSizeX));
1842*c8dee2aaSAndroid Build Coastguard Worker         this->write(", ");
1843*c8dee2aaSAndroid Build Coastguard Worker         this->write(std::to_string(fLocalSizeY));
1844*c8dee2aaSAndroid Build Coastguard Worker         this->write(", ");
1845*c8dee2aaSAndroid Build Coastguard Worker         this->write(std::to_string(fLocalSizeZ));
1846*c8dee2aaSAndroid Build Coastguard Worker         this->write(")");
1847*c8dee2aaSAndroid Build Coastguard Worker     } else {
1848*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(Position(), "program kind not supported");
1849*c8dee2aaSAndroid Build Coastguard Worker         return;
1850*c8dee2aaSAndroid Build Coastguard Worker     }
1851*c8dee2aaSAndroid Build Coastguard Worker 
1852*c8dee2aaSAndroid Build Coastguard Worker     this->write(" fn main(");
1853*c8dee2aaSAndroid Build Coastguard Worker     // The stage input struct is a parameter passed to main().
1854*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsStageInputStruct()) {
1855*c8dee2aaSAndroid Build Coastguard Worker         this->write("_stageIn: ");
1856*c8dee2aaSAndroid Build Coastguard Worker         this->write(pipeline_struct_prefix(programKind));
1857*c8dee2aaSAndroid Build Coastguard Worker         this->write("In");
1858*c8dee2aaSAndroid Build Coastguard Worker     }
1859*c8dee2aaSAndroid Build Coastguard Worker     // The stage output struct is returned from main().
1860*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsStageOutputStruct()) {
1861*c8dee2aaSAndroid Build Coastguard Worker         this->write(") -> ");
1862*c8dee2aaSAndroid Build Coastguard Worker         this->write(pipeline_struct_prefix(programKind));
1863*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("Out {");
1864*c8dee2aaSAndroid Build Coastguard Worker     } else {
1865*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(") {");
1866*c8dee2aaSAndroid Build Coastguard Worker     }
1867*c8dee2aaSAndroid Build Coastguard Worker     // Initialize polyfilled matrix uniforms if any were used.
1868*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
1869*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& [field, info] : fFieldPolyfillMap) {
1870*c8dee2aaSAndroid Build Coastguard Worker         if (info.fWasAccessed) {
1871*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("_skInitializePolyfilledUniforms();");
1872*c8dee2aaSAndroid Build Coastguard Worker             break;
1873*c8dee2aaSAndroid Build Coastguard Worker         }
1874*c8dee2aaSAndroid Build Coastguard Worker     }
1875*c8dee2aaSAndroid Build Coastguard Worker     // Declare the stage output struct.
1876*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsStageOutputStruct()) {
1877*c8dee2aaSAndroid Build Coastguard Worker         this->write("var _stageOut: ");
1878*c8dee2aaSAndroid Build Coastguard Worker         this->write(pipeline_struct_prefix(programKind));
1879*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("Out;");
1880*c8dee2aaSAndroid Build Coastguard Worker     }
1881*c8dee2aaSAndroid Build Coastguard Worker 
1882*c8dee2aaSAndroid Build Coastguard Worker     // We are compiling a Runtime Effect as a fragment shader, for testing purposes. We assign the
1883*c8dee2aaSAndroid Build Coastguard Worker     // result from _skslMain into sk_FragColor if the user-defined main returns a color. This
1884*c8dee2aaSAndroid Build Coastguard Worker     // doesn't actually matter, but it is more indicative of what a real program would do.
1885*c8dee2aaSAndroid Build Coastguard Worker     // `addImplicitFragColorWrite` from Transform::FindAndDeclareBuiltinVariables has already
1886*c8dee2aaSAndroid Build Coastguard Worker     // injected sk_FragColor into our stage outputs even if it wasn't explicitly referenced.
1887*c8dee2aaSAndroid Build Coastguard Worker     if (fGenSyntheticCode == IncludeSyntheticCode::kYes && ProgramConfig::IsFragment(programKind)) {
1888*c8dee2aaSAndroid Build Coastguard Worker         if (main.declaration().returnType().matches(*fContext.fTypes.fHalf4)) {
1889*c8dee2aaSAndroid Build Coastguard Worker             this->write("_stageOut.sk_FragColor = ");
1890*c8dee2aaSAndroid Build Coastguard Worker         }
1891*c8dee2aaSAndroid Build Coastguard Worker     }
1892*c8dee2aaSAndroid Build Coastguard Worker 
1893*c8dee2aaSAndroid Build Coastguard Worker     // Generate a function call to the user-defined main.
1894*c8dee2aaSAndroid Build Coastguard Worker     this->write("_skslMain(");
1895*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
1896*c8dee2aaSAndroid Build Coastguard Worker     WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&main.declaration());
1897*c8dee2aaSAndroid Build Coastguard Worker     if (deps) {
1898*c8dee2aaSAndroid Build Coastguard Worker         if (*deps & WGSLFunctionDependency::kPipelineInputs) {
1899*c8dee2aaSAndroid Build Coastguard Worker             this->write(separator());
1900*c8dee2aaSAndroid Build Coastguard Worker             this->write("_stageIn");
1901*c8dee2aaSAndroid Build Coastguard Worker         }
1902*c8dee2aaSAndroid Build Coastguard Worker         if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
1903*c8dee2aaSAndroid Build Coastguard Worker             this->write(separator());
1904*c8dee2aaSAndroid Build Coastguard Worker             this->write("&_stageOut");
1905*c8dee2aaSAndroid Build Coastguard Worker         }
1906*c8dee2aaSAndroid Build Coastguard Worker     }
1907*c8dee2aaSAndroid Build Coastguard Worker 
1908*c8dee2aaSAndroid Build Coastguard Worker     if (fGenSyntheticCode == IncludeSyntheticCode::kYes) {
1909*c8dee2aaSAndroid Build Coastguard Worker         if (const Variable* v = main.declaration().getMainCoordsParameter()) {
1910*c8dee2aaSAndroid Build Coastguard Worker             // We are compiling a Runtime Effect as a fragment shader, for testing purposes.
1911*c8dee2aaSAndroid Build Coastguard Worker             // We need to synthesize a coordinates parameter, but the coordinates don't matter.
1912*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(ProgramConfig::IsFragment(programKind));
1913*c8dee2aaSAndroid Build Coastguard Worker             const Type& type = v->type();
1914*c8dee2aaSAndroid Build Coastguard Worker             if (!type.matches(*fContext.fTypes.fFloat2)) {
1915*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(
1916*c8dee2aaSAndroid Build Coastguard Worker                         main.fPosition,
1917*c8dee2aaSAndroid Build Coastguard Worker                         "main function has unsupported parameter: " + type.description());
1918*c8dee2aaSAndroid Build Coastguard Worker                 return;
1919*c8dee2aaSAndroid Build Coastguard Worker             }
1920*c8dee2aaSAndroid Build Coastguard Worker             this->write(separator());
1921*c8dee2aaSAndroid Build Coastguard Worker             this->write("/*fragcoord*/ vec2<f32>()");
1922*c8dee2aaSAndroid Build Coastguard Worker         }
1923*c8dee2aaSAndroid Build Coastguard Worker     }
1924*c8dee2aaSAndroid Build Coastguard Worker 
1925*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(");");
1926*c8dee2aaSAndroid Build Coastguard Worker 
1927*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsStageOutputStruct()) {
1928*c8dee2aaSAndroid Build Coastguard Worker         // Return the stage output struct.
1929*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("return _stageOut;");
1930*c8dee2aaSAndroid Build Coastguard Worker     }
1931*c8dee2aaSAndroid Build Coastguard Worker 
1932*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
1933*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
1934*c8dee2aaSAndroid Build Coastguard Worker }
1935*c8dee2aaSAndroid Build Coastguard Worker 
writeStatement(const Statement & s)1936*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeStatement(const Statement& s) {
1937*c8dee2aaSAndroid Build Coastguard Worker     switch (s.kind()) {
1938*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBlock:
1939*c8dee2aaSAndroid Build Coastguard Worker             this->writeBlock(s.as<Block>());
1940*c8dee2aaSAndroid Build Coastguard Worker             break;
1941*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBreak:
1942*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("break;");
1943*c8dee2aaSAndroid Build Coastguard Worker             break;
1944*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kContinue:
1945*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("continue;");
1946*c8dee2aaSAndroid Build Coastguard Worker             break;
1947*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kDiscard:
1948*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("discard;");
1949*c8dee2aaSAndroid Build Coastguard Worker             break;
1950*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kDo:
1951*c8dee2aaSAndroid Build Coastguard Worker             this->writeDoStatement(s.as<DoStatement>());
1952*c8dee2aaSAndroid Build Coastguard Worker             break;
1953*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kExpression:
1954*c8dee2aaSAndroid Build Coastguard Worker             this->writeExpressionStatement(*s.as<ExpressionStatement>().expression());
1955*c8dee2aaSAndroid Build Coastguard Worker             break;
1956*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kFor:
1957*c8dee2aaSAndroid Build Coastguard Worker             this->writeForStatement(s.as<ForStatement>());
1958*c8dee2aaSAndroid Build Coastguard Worker             break;
1959*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kIf:
1960*c8dee2aaSAndroid Build Coastguard Worker             this->writeIfStatement(s.as<IfStatement>());
1961*c8dee2aaSAndroid Build Coastguard Worker             break;
1962*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kNop:
1963*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(";");
1964*c8dee2aaSAndroid Build Coastguard Worker             break;
1965*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kReturn:
1966*c8dee2aaSAndroid Build Coastguard Worker             this->writeReturnStatement(s.as<ReturnStatement>());
1967*c8dee2aaSAndroid Build Coastguard Worker             break;
1968*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kSwitch:
1969*c8dee2aaSAndroid Build Coastguard Worker             this->writeSwitchStatement(s.as<SwitchStatement>());
1970*c8dee2aaSAndroid Build Coastguard Worker             break;
1971*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kSwitchCase:
1972*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("switch-case statements should only be present inside a switch");
1973*c8dee2aaSAndroid Build Coastguard Worker             break;
1974*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kVarDeclaration:
1975*c8dee2aaSAndroid Build Coastguard Worker             this->writeVarDeclaration(s.as<VarDeclaration>());
1976*c8dee2aaSAndroid Build Coastguard Worker             break;
1977*c8dee2aaSAndroid Build Coastguard Worker     }
1978*c8dee2aaSAndroid Build Coastguard Worker }
1979*c8dee2aaSAndroid Build Coastguard Worker 
writeStatements(const StatementArray & statements)1980*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeStatements(const StatementArray& statements) {
1981*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& s : statements) {
1982*c8dee2aaSAndroid Build Coastguard Worker         if (!s->isEmpty()) {
1983*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*s);
1984*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
1985*c8dee2aaSAndroid Build Coastguard Worker         }
1986*c8dee2aaSAndroid Build Coastguard Worker     }
1987*c8dee2aaSAndroid Build Coastguard Worker }
1988*c8dee2aaSAndroid Build Coastguard Worker 
writeBlock(const Block & b)1989*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeBlock(const Block& b) {
1990*c8dee2aaSAndroid Build Coastguard Worker     // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
1991*c8dee2aaSAndroid Build Coastguard Worker     // something here to make the code valid).
1992*c8dee2aaSAndroid Build Coastguard Worker     bool isScope = b.isScope() || b.isEmpty();
1993*c8dee2aaSAndroid Build Coastguard Worker     if (isScope) {
1994*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("{");
1995*c8dee2aaSAndroid Build Coastguard Worker         fIndentation++;
1996*c8dee2aaSAndroid Build Coastguard Worker     }
1997*c8dee2aaSAndroid Build Coastguard Worker     this->writeStatements(b.children());
1998*c8dee2aaSAndroid Build Coastguard Worker     if (isScope) {
1999*c8dee2aaSAndroid Build Coastguard Worker         fIndentation--;
2000*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
2001*c8dee2aaSAndroid Build Coastguard Worker     }
2002*c8dee2aaSAndroid Build Coastguard Worker }
2003*c8dee2aaSAndroid Build Coastguard Worker 
writeExpressionStatement(const Expression & expr)2004*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeExpressionStatement(const Expression& expr) {
2005*c8dee2aaSAndroid Build Coastguard Worker     // Any expression-related side effects must be emitted as separate statements when
2006*c8dee2aaSAndroid Build Coastguard Worker     // `assembleExpression` is called.
2007*c8dee2aaSAndroid Build Coastguard Worker     // The final result of the expression will be a variable, let-reference, or an expression with
2008*c8dee2aaSAndroid Build Coastguard Worker     // no side effects (`foo + bar`). Discarding this result is safe, as the program never uses it.
2009*c8dee2aaSAndroid Build Coastguard Worker     (void)this->assembleExpression(expr, Precedence::kStatement);
2010*c8dee2aaSAndroid Build Coastguard Worker }
2011*c8dee2aaSAndroid Build Coastguard Worker 
writeDoStatement(const DoStatement & s)2012*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeDoStatement(const DoStatement& s) {
2013*c8dee2aaSAndroid Build Coastguard Worker     // Generate a loop structure like this:
2014*c8dee2aaSAndroid Build Coastguard Worker     //   loop {
2015*c8dee2aaSAndroid Build Coastguard Worker     //       body-statement;
2016*c8dee2aaSAndroid Build Coastguard Worker     //       continuing {
2017*c8dee2aaSAndroid Build Coastguard Worker     //           break if inverted-test-expression;
2018*c8dee2aaSAndroid Build Coastguard Worker     //       }
2019*c8dee2aaSAndroid Build Coastguard Worker     //   }
2020*c8dee2aaSAndroid Build Coastguard Worker 
2021*c8dee2aaSAndroid Build Coastguard Worker     ++fConditionalScopeDepth;
2022*c8dee2aaSAndroid Build Coastguard Worker 
2023*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> invertedTestExpr = PrefixExpression::Make(
2024*c8dee2aaSAndroid Build Coastguard Worker             fContext, s.test()->fPosition, OperatorKind::LOGICALNOT, s.test()->clone());
2025*c8dee2aaSAndroid Build Coastguard Worker 
2026*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("loop {");
2027*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
2028*c8dee2aaSAndroid Build Coastguard Worker     this->writeStatement(*s.statement());
2029*c8dee2aaSAndroid Build Coastguard Worker     this->finishLine();
2030*c8dee2aaSAndroid Build Coastguard Worker 
2031*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("continuing {");
2032*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
2033*c8dee2aaSAndroid Build Coastguard Worker     std::string breakIfExpr = this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
2034*c8dee2aaSAndroid Build Coastguard Worker     this->write("break if ");
2035*c8dee2aaSAndroid Build Coastguard Worker     this->write(breakIfExpr);
2036*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(";");
2037*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
2038*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2039*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
2040*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2041*c8dee2aaSAndroid Build Coastguard Worker 
2042*c8dee2aaSAndroid Build Coastguard Worker     --fConditionalScopeDepth;
2043*c8dee2aaSAndroid Build Coastguard Worker }
2044*c8dee2aaSAndroid Build Coastguard Worker 
writeForStatement(const ForStatement & s)2045*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeForStatement(const ForStatement& s) {
2046*c8dee2aaSAndroid Build Coastguard Worker     // Generate a loop structure wrapped in an extra scope:
2047*c8dee2aaSAndroid Build Coastguard Worker     //   {
2048*c8dee2aaSAndroid Build Coastguard Worker     //     initializer-statement;
2049*c8dee2aaSAndroid Build Coastguard Worker     //     loop;
2050*c8dee2aaSAndroid Build Coastguard Worker     //   }
2051*c8dee2aaSAndroid Build Coastguard Worker     // The outer scope is necessary to prevent the initializer-variable from leaking out into the
2052*c8dee2aaSAndroid Build Coastguard Worker     // rest of the code. In practice, the generated code actually tends to be scoped even more
2053*c8dee2aaSAndroid Build Coastguard Worker     // deeply, as the body-statement almost always contributes an extra block.
2054*c8dee2aaSAndroid Build Coastguard Worker 
2055*c8dee2aaSAndroid Build Coastguard Worker     ++fConditionalScopeDepth;
2056*c8dee2aaSAndroid Build Coastguard Worker 
2057*c8dee2aaSAndroid Build Coastguard Worker     if (s.initializer()) {
2058*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("{");
2059*c8dee2aaSAndroid Build Coastguard Worker         fIndentation++;
2060*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*s.initializer());
2061*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine();
2062*c8dee2aaSAndroid Build Coastguard Worker     }
2063*c8dee2aaSAndroid Build Coastguard Worker 
2064*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("loop {");
2065*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
2066*c8dee2aaSAndroid Build Coastguard Worker 
2067*c8dee2aaSAndroid Build Coastguard Worker     if (s.unrollInfo()) {
2068*c8dee2aaSAndroid Build Coastguard Worker         if (s.unrollInfo()->fCount <= 0) {
2069*c8dee2aaSAndroid Build Coastguard Worker             // Loops which are known to never execute don't need to be emitted at all.
2070*c8dee2aaSAndroid Build Coastguard Worker             // (However, the front end should have already replaced this loop with a Nop.)
2071*c8dee2aaSAndroid Build Coastguard Worker         } else {
2072*c8dee2aaSAndroid Build Coastguard Worker             // Loops which are known to execute at least once can use this form:
2073*c8dee2aaSAndroid Build Coastguard Worker             //
2074*c8dee2aaSAndroid Build Coastguard Worker             //     loop {
2075*c8dee2aaSAndroid Build Coastguard Worker             //         body-statement;
2076*c8dee2aaSAndroid Build Coastguard Worker             //         continuing {
2077*c8dee2aaSAndroid Build Coastguard Worker             //             next-expression;
2078*c8dee2aaSAndroid Build Coastguard Worker             //             break if inverted-test-expression;
2079*c8dee2aaSAndroid Build Coastguard Worker             //         }
2080*c8dee2aaSAndroid Build Coastguard Worker             //     }
2081*c8dee2aaSAndroid Build Coastguard Worker 
2082*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*s.statement());
2083*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2084*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("continuing {");
2085*c8dee2aaSAndroid Build Coastguard Worker             ++fIndentation;
2086*c8dee2aaSAndroid Build Coastguard Worker 
2087*c8dee2aaSAndroid Build Coastguard Worker             if (s.next()) {
2088*c8dee2aaSAndroid Build Coastguard Worker                 this->writeExpressionStatement(*s.next());
2089*c8dee2aaSAndroid Build Coastguard Worker                 this->finishLine();
2090*c8dee2aaSAndroid Build Coastguard Worker             }
2091*c8dee2aaSAndroid Build Coastguard Worker 
2092*c8dee2aaSAndroid Build Coastguard Worker             if (s.test()) {
2093*c8dee2aaSAndroid Build Coastguard Worker                 std::unique_ptr<Expression> invertedTestExpr = PrefixExpression::Make(
2094*c8dee2aaSAndroid Build Coastguard Worker                         fContext, s.test()->fPosition, OperatorKind::LOGICALNOT, s.test()->clone());
2095*c8dee2aaSAndroid Build Coastguard Worker 
2096*c8dee2aaSAndroid Build Coastguard Worker                 std::string breakIfExpr =
2097*c8dee2aaSAndroid Build Coastguard Worker                         this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
2098*c8dee2aaSAndroid Build Coastguard Worker                 this->write("break if ");
2099*c8dee2aaSAndroid Build Coastguard Worker                 this->write(breakIfExpr);
2100*c8dee2aaSAndroid Build Coastguard Worker                 this->writeLine(";");
2101*c8dee2aaSAndroid Build Coastguard Worker             }
2102*c8dee2aaSAndroid Build Coastguard Worker 
2103*c8dee2aaSAndroid Build Coastguard Worker             --fIndentation;
2104*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("}");
2105*c8dee2aaSAndroid Build Coastguard Worker         }
2106*c8dee2aaSAndroid Build Coastguard Worker     } else {
2107*c8dee2aaSAndroid Build Coastguard Worker         // Loops without a known execution count are emitted in this form:
2108*c8dee2aaSAndroid Build Coastguard Worker         //
2109*c8dee2aaSAndroid Build Coastguard Worker         //     loop {
2110*c8dee2aaSAndroid Build Coastguard Worker         //         if test-expression {
2111*c8dee2aaSAndroid Build Coastguard Worker         //             body-statement;
2112*c8dee2aaSAndroid Build Coastguard Worker         //         } else {
2113*c8dee2aaSAndroid Build Coastguard Worker         //             break;
2114*c8dee2aaSAndroid Build Coastguard Worker         //         }
2115*c8dee2aaSAndroid Build Coastguard Worker         //         continuing {
2116*c8dee2aaSAndroid Build Coastguard Worker         //             next-expression;
2117*c8dee2aaSAndroid Build Coastguard Worker         //         }
2118*c8dee2aaSAndroid Build Coastguard Worker         //     }
2119*c8dee2aaSAndroid Build Coastguard Worker 
2120*c8dee2aaSAndroid Build Coastguard Worker         if (s.test()) {
2121*c8dee2aaSAndroid Build Coastguard Worker             std::string testExpr = this->assembleExpression(*s.test(), Precedence::kExpression);
2122*c8dee2aaSAndroid Build Coastguard Worker             this->write("if ");
2123*c8dee2aaSAndroid Build Coastguard Worker             this->write(testExpr);
2124*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(" {");
2125*c8dee2aaSAndroid Build Coastguard Worker 
2126*c8dee2aaSAndroid Build Coastguard Worker             fIndentation++;
2127*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*s.statement());
2128*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2129*c8dee2aaSAndroid Build Coastguard Worker             fIndentation--;
2130*c8dee2aaSAndroid Build Coastguard Worker 
2131*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("} else {");
2132*c8dee2aaSAndroid Build Coastguard Worker 
2133*c8dee2aaSAndroid Build Coastguard Worker             fIndentation++;
2134*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("break;");
2135*c8dee2aaSAndroid Build Coastguard Worker             fIndentation--;
2136*c8dee2aaSAndroid Build Coastguard Worker 
2137*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("}");
2138*c8dee2aaSAndroid Build Coastguard Worker         }
2139*c8dee2aaSAndroid Build Coastguard Worker         else {
2140*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*s.statement());
2141*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2142*c8dee2aaSAndroid Build Coastguard Worker         }
2143*c8dee2aaSAndroid Build Coastguard Worker 
2144*c8dee2aaSAndroid Build Coastguard Worker         if (s.next()) {
2145*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("continuing {");
2146*c8dee2aaSAndroid Build Coastguard Worker             fIndentation++;
2147*c8dee2aaSAndroid Build Coastguard Worker             this->writeExpressionStatement(*s.next());
2148*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2149*c8dee2aaSAndroid Build Coastguard Worker             fIndentation--;
2150*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("}");
2151*c8dee2aaSAndroid Build Coastguard Worker         }
2152*c8dee2aaSAndroid Build Coastguard Worker     }
2153*c8dee2aaSAndroid Build Coastguard Worker 
2154*c8dee2aaSAndroid Build Coastguard Worker     // This matches an open-brace at the top of the loop.
2155*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
2156*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2157*c8dee2aaSAndroid Build Coastguard Worker 
2158*c8dee2aaSAndroid Build Coastguard Worker     if (s.initializer()) {
2159*c8dee2aaSAndroid Build Coastguard Worker         // This matches an open-brace before the initializer-statement.
2160*c8dee2aaSAndroid Build Coastguard Worker         fIndentation--;
2161*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
2162*c8dee2aaSAndroid Build Coastguard Worker     }
2163*c8dee2aaSAndroid Build Coastguard Worker 
2164*c8dee2aaSAndroid Build Coastguard Worker     --fConditionalScopeDepth;
2165*c8dee2aaSAndroid Build Coastguard Worker }
2166*c8dee2aaSAndroid Build Coastguard Worker 
writeIfStatement(const IfStatement & s)2167*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeIfStatement(const IfStatement& s) {
2168*c8dee2aaSAndroid Build Coastguard Worker     ++fConditionalScopeDepth;
2169*c8dee2aaSAndroid Build Coastguard Worker 
2170*c8dee2aaSAndroid Build Coastguard Worker     std::string testExpr = this->assembleExpression(*s.test(), Precedence::kExpression);
2171*c8dee2aaSAndroid Build Coastguard Worker     this->write("if ");
2172*c8dee2aaSAndroid Build Coastguard Worker     this->write(testExpr);
2173*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(" {");
2174*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
2175*c8dee2aaSAndroid Build Coastguard Worker     this->writeStatement(*s.ifTrue());
2176*c8dee2aaSAndroid Build Coastguard Worker     this->finishLine();
2177*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
2178*c8dee2aaSAndroid Build Coastguard Worker     if (s.ifFalse()) {
2179*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("} else {");
2180*c8dee2aaSAndroid Build Coastguard Worker         fIndentation++;
2181*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*s.ifFalse());
2182*c8dee2aaSAndroid Build Coastguard Worker         this->finishLine();
2183*c8dee2aaSAndroid Build Coastguard Worker         fIndentation--;
2184*c8dee2aaSAndroid Build Coastguard Worker     }
2185*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2186*c8dee2aaSAndroid Build Coastguard Worker 
2187*c8dee2aaSAndroid Build Coastguard Worker     --fConditionalScopeDepth;
2188*c8dee2aaSAndroid Build Coastguard Worker }
2189*c8dee2aaSAndroid Build Coastguard Worker 
writeReturnStatement(const ReturnStatement & s)2190*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeReturnStatement(const ReturnStatement& s) {
2191*c8dee2aaSAndroid Build Coastguard Worker     fHasUnconditionalReturn |= (fConditionalScopeDepth == 0);
2192*c8dee2aaSAndroid Build Coastguard Worker 
2193*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = s.expression()
2194*c8dee2aaSAndroid Build Coastguard Worker                                ? this->assembleExpression(*s.expression(), Precedence::kExpression)
2195*c8dee2aaSAndroid Build Coastguard Worker                                : std::string();
2196*c8dee2aaSAndroid Build Coastguard Worker     this->write("return ");
2197*c8dee2aaSAndroid Build Coastguard Worker     this->write(expr);
2198*c8dee2aaSAndroid Build Coastguard Worker     this->write(";");
2199*c8dee2aaSAndroid Build Coastguard Worker }
2200*c8dee2aaSAndroid Build Coastguard Worker 
writeSwitchCaseList(SkSpan<const SwitchCase * const> cases)2201*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeSwitchCaseList(SkSpan<const SwitchCase* const> cases) {
2202*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
2203*c8dee2aaSAndroid Build Coastguard Worker     for (const SwitchCase* const sc : cases) {
2204*c8dee2aaSAndroid Build Coastguard Worker         this->write(separator());
2205*c8dee2aaSAndroid Build Coastguard Worker         if (sc->isDefault()) {
2206*c8dee2aaSAndroid Build Coastguard Worker             this->write("default");
2207*c8dee2aaSAndroid Build Coastguard Worker         } else {
2208*c8dee2aaSAndroid Build Coastguard Worker             this->write(std::to_string(sc->value()));
2209*c8dee2aaSAndroid Build Coastguard Worker         }
2210*c8dee2aaSAndroid Build Coastguard Worker     }
2211*c8dee2aaSAndroid Build Coastguard Worker }
2212*c8dee2aaSAndroid Build Coastguard Worker 
writeSwitchCases(SkSpan<const SwitchCase * const> cases)2213*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeSwitchCases(SkSpan<const SwitchCase* const> cases) {
2214*c8dee2aaSAndroid Build Coastguard Worker     if (!cases.empty()) {
2215*c8dee2aaSAndroid Build Coastguard Worker         // Only the last switch-case should have a non-empty statement.
2216*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(std::all_of(cases.begin(), std::prev(cases.end()), [](const SwitchCase* sc) {
2217*c8dee2aaSAndroid Build Coastguard Worker             return sc->statement()->isEmpty();
2218*c8dee2aaSAndroid Build Coastguard Worker         }));
2219*c8dee2aaSAndroid Build Coastguard Worker 
2220*c8dee2aaSAndroid Build Coastguard Worker         // Emit the cases in a comma-separated list.
2221*c8dee2aaSAndroid Build Coastguard Worker         this->write("case ");
2222*c8dee2aaSAndroid Build Coastguard Worker         this->writeSwitchCaseList(cases);
2223*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" {");
2224*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
2225*c8dee2aaSAndroid Build Coastguard Worker 
2226*c8dee2aaSAndroid Build Coastguard Worker         // Emit the switch-case body.
2227*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*cases.back()->statement());
2228*c8dee2aaSAndroid Build Coastguard Worker         this->finishLine();
2229*c8dee2aaSAndroid Build Coastguard Worker 
2230*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
2231*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
2232*c8dee2aaSAndroid Build Coastguard Worker     }
2233*c8dee2aaSAndroid Build Coastguard Worker }
2234*c8dee2aaSAndroid Build Coastguard Worker 
writeEmulatedSwitchFallthroughCases(SkSpan<const SwitchCase * const> cases,std::string_view switchValue)2235*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeEmulatedSwitchFallthroughCases(SkSpan<const SwitchCase* const> cases,
2236*c8dee2aaSAndroid Build Coastguard Worker                                                             std::string_view switchValue) {
2237*c8dee2aaSAndroid Build Coastguard Worker     // There's no need for fallthrough handling unless we actually have multiple case blocks.
2238*c8dee2aaSAndroid Build Coastguard Worker     if (cases.size() < 2) {
2239*c8dee2aaSAndroid Build Coastguard Worker         this->writeSwitchCases(cases);
2240*c8dee2aaSAndroid Build Coastguard Worker         return;
2241*c8dee2aaSAndroid Build Coastguard Worker     }
2242*c8dee2aaSAndroid Build Coastguard Worker 
2243*c8dee2aaSAndroid Build Coastguard Worker     // Match against the entire case group.
2244*c8dee2aaSAndroid Build Coastguard Worker     this->write("case ");
2245*c8dee2aaSAndroid Build Coastguard Worker     this->writeSwitchCaseList(cases);
2246*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(" {");
2247*c8dee2aaSAndroid Build Coastguard Worker     ++fIndentation;
2248*c8dee2aaSAndroid Build Coastguard Worker 
2249*c8dee2aaSAndroid Build Coastguard Worker     std::string fallthroughVar = this->writeScratchVar(*fContext.fTypes.fBool, "false");
2250*c8dee2aaSAndroid Build Coastguard Worker     const size_t secondToLastCaseIndex = cases.size() - 2;
2251*c8dee2aaSAndroid Build Coastguard Worker     const size_t lastCaseIndex = cases.size() - 1;
2252*c8dee2aaSAndroid Build Coastguard Worker 
2253*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < cases.size(); ++index) {
2254*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& sc = *cases[index];
2255*c8dee2aaSAndroid Build Coastguard Worker         if (index < lastCaseIndex) {
2256*c8dee2aaSAndroid Build Coastguard Worker             // The default case must come last in SkSL, and this case isn't the last one, so it
2257*c8dee2aaSAndroid Build Coastguard Worker             // can't possibly be the default.
2258*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!sc.isDefault());
2259*c8dee2aaSAndroid Build Coastguard Worker 
2260*c8dee2aaSAndroid Build Coastguard Worker             this->write("if ");
2261*c8dee2aaSAndroid Build Coastguard Worker             if (index > 0) {
2262*c8dee2aaSAndroid Build Coastguard Worker                 this->write(fallthroughVar);
2263*c8dee2aaSAndroid Build Coastguard Worker                 this->write(" || ");
2264*c8dee2aaSAndroid Build Coastguard Worker             }
2265*c8dee2aaSAndroid Build Coastguard Worker             this->write(switchValue);
2266*c8dee2aaSAndroid Build Coastguard Worker             this->write(" == ");
2267*c8dee2aaSAndroid Build Coastguard Worker             this->write(std::to_string(sc.value()));
2268*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(" {");
2269*c8dee2aaSAndroid Build Coastguard Worker             fIndentation++;
2270*c8dee2aaSAndroid Build Coastguard Worker 
2271*c8dee2aaSAndroid Build Coastguard Worker             // We write the entire case-block statement here, and then set `switchFallthrough`
2272*c8dee2aaSAndroid Build Coastguard Worker             // to 1. If the case-block had a break statement in it, we break out of the outer
2273*c8dee2aaSAndroid Build Coastguard Worker             // for-loop entirely, meaning the `switchFallthrough` assignment never occurs, nor
2274*c8dee2aaSAndroid Build Coastguard Worker             // does any code after it inside the switch. We've forbidden `continue` statements
2275*c8dee2aaSAndroid Build Coastguard Worker             // inside switch case-blocks entirely, so we don't need to consider their effect on
2276*c8dee2aaSAndroid Build Coastguard Worker             // control flow; see the Finalizer in FunctionDefinition::Convert.
2277*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*sc.statement());
2278*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2279*c8dee2aaSAndroid Build Coastguard Worker 
2280*c8dee2aaSAndroid Build Coastguard Worker             if (index < secondToLastCaseIndex) {
2281*c8dee2aaSAndroid Build Coastguard Worker                 // Set a variable to indicate falling through to the next block. The very last
2282*c8dee2aaSAndroid Build Coastguard Worker                 // case-block is reached by process of elimination and doesn't need this
2283*c8dee2aaSAndroid Build Coastguard Worker                 // variable, so we don't actually need to set it if we are on the second-to-last
2284*c8dee2aaSAndroid Build Coastguard Worker                 // case block.
2285*c8dee2aaSAndroid Build Coastguard Worker                 this->write(fallthroughVar);
2286*c8dee2aaSAndroid Build Coastguard Worker                 this->write(" = true;  ");
2287*c8dee2aaSAndroid Build Coastguard Worker             }
2288*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("// fallthrough");
2289*c8dee2aaSAndroid Build Coastguard Worker 
2290*c8dee2aaSAndroid Build Coastguard Worker             fIndentation--;
2291*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine("}");
2292*c8dee2aaSAndroid Build Coastguard Worker         } else {
2293*c8dee2aaSAndroid Build Coastguard Worker             // This is the final case. Since it's always last, we can just dump in the code.
2294*c8dee2aaSAndroid Build Coastguard Worker             // (If we didn't match any of the other values, we must have matched this one by
2295*c8dee2aaSAndroid Build Coastguard Worker             // process of elimination. If we did match one of the other values, we either hit a
2296*c8dee2aaSAndroid Build Coastguard Worker             // `break` statement earlier--and won't get this far--or we're falling through.)
2297*c8dee2aaSAndroid Build Coastguard Worker             this->writeStatement(*sc.statement());
2298*c8dee2aaSAndroid Build Coastguard Worker             this->finishLine();
2299*c8dee2aaSAndroid Build Coastguard Worker         }
2300*c8dee2aaSAndroid Build Coastguard Worker     }
2301*c8dee2aaSAndroid Build Coastguard Worker 
2302*c8dee2aaSAndroid Build Coastguard Worker     --fIndentation;
2303*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2304*c8dee2aaSAndroid Build Coastguard Worker }
2305*c8dee2aaSAndroid Build Coastguard Worker 
writeSwitchStatement(const SwitchStatement & s)2306*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
2307*c8dee2aaSAndroid Build Coastguard Worker     // WGSL supports the `switch` statement in a limited capacity. A default case must always be
2308*c8dee2aaSAndroid Build Coastguard Worker     // specified. Each switch-case must be scoped inside braces. Fallthrough is not supported; a
2309*c8dee2aaSAndroid Build Coastguard Worker     // trailing break is implied at the end of each switch-case block. (Explicit breaks are also
2310*c8dee2aaSAndroid Build Coastguard Worker     // allowed.)  One minor improvement over a traditional switch is that switch-cases take a list
2311*c8dee2aaSAndroid Build Coastguard Worker     // of values to match, instead of a single value:
2312*c8dee2aaSAndroid Build Coastguard Worker     //   case 1, 2       { foo(); }
2313*c8dee2aaSAndroid Build Coastguard Worker     //   case 3, default { bar(); }
2314*c8dee2aaSAndroid Build Coastguard Worker     //
2315*c8dee2aaSAndroid Build Coastguard Worker     // We will use the native WGSL switch statement for any switch-cases in the SkSL which can be
2316*c8dee2aaSAndroid Build Coastguard Worker     // made to conform to these limitations. The remaining cases which cannot conform will be
2317*c8dee2aaSAndroid Build Coastguard Worker     // emulated with if-else blocks (similar to our GLSL ES2 switch-statement emulation path). This
2318*c8dee2aaSAndroid Build Coastguard Worker     // should give us good performance in the common case, since most switches naturally conform.
2319*c8dee2aaSAndroid Build Coastguard Worker 
2320*c8dee2aaSAndroid Build Coastguard Worker     // First, let's emit the switch itself.
2321*c8dee2aaSAndroid Build Coastguard Worker     std::string valueExpr = this->writeNontrivialScratchLet(*s.value(), Precedence::kExpression);
2322*c8dee2aaSAndroid Build Coastguard Worker     this->write("switch ");
2323*c8dee2aaSAndroid Build Coastguard Worker     this->write(valueExpr);
2324*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(" {");
2325*c8dee2aaSAndroid Build Coastguard Worker     ++fIndentation;
2326*c8dee2aaSAndroid Build Coastguard Worker 
2327*c8dee2aaSAndroid Build Coastguard Worker     // Now let's go through the switch-cases, and emit the ones that don't fall through.
2328*c8dee2aaSAndroid Build Coastguard Worker     TArray<const SwitchCase*> nativeCases;
2329*c8dee2aaSAndroid Build Coastguard Worker     TArray<const SwitchCase*> fallthroughCases;
2330*c8dee2aaSAndroid Build Coastguard Worker     bool previousCaseFellThrough = false;
2331*c8dee2aaSAndroid Build Coastguard Worker     bool foundNativeDefault = false;
2332*c8dee2aaSAndroid Build Coastguard Worker     [[maybe_unused]] bool foundFallthroughDefault = false;
2333*c8dee2aaSAndroid Build Coastguard Worker 
2334*c8dee2aaSAndroid Build Coastguard Worker     const int lastSwitchCaseIdx = s.cases().size() - 1;
2335*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index <= lastSwitchCaseIdx; ++index) {
2336*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& sc = s.cases()[index]->as<SwitchCase>();
2337*c8dee2aaSAndroid Build Coastguard Worker 
2338*c8dee2aaSAndroid Build Coastguard Worker         if (sc.statement()->isEmpty()) {
2339*c8dee2aaSAndroid Build Coastguard Worker             // This is a `case X:` that immediately falls through to the next case.
2340*c8dee2aaSAndroid Build Coastguard Worker             // If we aren't already falling through, we can handle this via a comma-separated list.
2341*c8dee2aaSAndroid Build Coastguard Worker             if (previousCaseFellThrough) {
2342*c8dee2aaSAndroid Build Coastguard Worker                 fallthroughCases.push_back(&sc);
2343*c8dee2aaSAndroid Build Coastguard Worker                 foundFallthroughDefault |= sc.isDefault();
2344*c8dee2aaSAndroid Build Coastguard Worker             } else {
2345*c8dee2aaSAndroid Build Coastguard Worker                 nativeCases.push_back(&sc);
2346*c8dee2aaSAndroid Build Coastguard Worker                 foundNativeDefault |= sc.isDefault();
2347*c8dee2aaSAndroid Build Coastguard Worker             }
2348*c8dee2aaSAndroid Build Coastguard Worker             continue;
2349*c8dee2aaSAndroid Build Coastguard Worker         }
2350*c8dee2aaSAndroid Build Coastguard Worker 
2351*c8dee2aaSAndroid Build Coastguard Worker         if (index == lastSwitchCaseIdx || Analysis::SwitchCaseContainsUnconditionalExit(sc)) {
2352*c8dee2aaSAndroid Build Coastguard Worker             // This is a `case X:` that never falls through.
2353*c8dee2aaSAndroid Build Coastguard Worker             if (previousCaseFellThrough) {
2354*c8dee2aaSAndroid Build Coastguard Worker                 // Because the previous cases fell through, we can't use a native switch-case here.
2355*c8dee2aaSAndroid Build Coastguard Worker                 fallthroughCases.push_back(&sc);
2356*c8dee2aaSAndroid Build Coastguard Worker                 foundFallthroughDefault |= sc.isDefault();
2357*c8dee2aaSAndroid Build Coastguard Worker 
2358*c8dee2aaSAndroid Build Coastguard Worker                 this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2359*c8dee2aaSAndroid Build Coastguard Worker                 fallthroughCases.clear();
2360*c8dee2aaSAndroid Build Coastguard Worker 
2361*c8dee2aaSAndroid Build Coastguard Worker                 // Fortunately, we're no longer falling through blocks, so we might be able to use a
2362*c8dee2aaSAndroid Build Coastguard Worker                 // native switch-case list again.
2363*c8dee2aaSAndroid Build Coastguard Worker                 previousCaseFellThrough = false;
2364*c8dee2aaSAndroid Build Coastguard Worker             } else {
2365*c8dee2aaSAndroid Build Coastguard Worker                 // Emit a native switch-case block with a comma-separated case list.
2366*c8dee2aaSAndroid Build Coastguard Worker                 nativeCases.push_back(&sc);
2367*c8dee2aaSAndroid Build Coastguard Worker                 foundNativeDefault |= sc.isDefault();
2368*c8dee2aaSAndroid Build Coastguard Worker 
2369*c8dee2aaSAndroid Build Coastguard Worker                 this->writeSwitchCases(nativeCases);
2370*c8dee2aaSAndroid Build Coastguard Worker                 nativeCases.clear();
2371*c8dee2aaSAndroid Build Coastguard Worker             }
2372*c8dee2aaSAndroid Build Coastguard Worker             continue;
2373*c8dee2aaSAndroid Build Coastguard Worker         }
2374*c8dee2aaSAndroid Build Coastguard Worker 
2375*c8dee2aaSAndroid Build Coastguard Worker         // This case falls through, so it will need to be handled via emulation.
2376*c8dee2aaSAndroid Build Coastguard Worker         // If we have put together a collection of "native" cases (cases that fall through with no
2377*c8dee2aaSAndroid Build Coastguard Worker         // actual case-body), we will need to slide them over into the fallthrough-case list.
2378*c8dee2aaSAndroid Build Coastguard Worker         fallthroughCases.push_back_n(nativeCases.size(), nativeCases.data());
2379*c8dee2aaSAndroid Build Coastguard Worker         nativeCases.clear();
2380*c8dee2aaSAndroid Build Coastguard Worker 
2381*c8dee2aaSAndroid Build Coastguard Worker         fallthroughCases.push_back(&sc);
2382*c8dee2aaSAndroid Build Coastguard Worker         foundFallthroughDefault |= sc.isDefault();
2383*c8dee2aaSAndroid Build Coastguard Worker         previousCaseFellThrough = true;
2384*c8dee2aaSAndroid Build Coastguard Worker     }
2385*c8dee2aaSAndroid Build Coastguard Worker 
2386*c8dee2aaSAndroid Build Coastguard Worker     // Finish out the remaining switch-cases.
2387*c8dee2aaSAndroid Build Coastguard Worker     this->writeSwitchCases(nativeCases);
2388*c8dee2aaSAndroid Build Coastguard Worker     nativeCases.clear();
2389*c8dee2aaSAndroid Build Coastguard Worker 
2390*c8dee2aaSAndroid Build Coastguard Worker     this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2391*c8dee2aaSAndroid Build Coastguard Worker     fallthroughCases.clear();
2392*c8dee2aaSAndroid Build Coastguard Worker 
2393*c8dee2aaSAndroid Build Coastguard Worker     // WGSL requires a default case.
2394*c8dee2aaSAndroid Build Coastguard Worker     if (!foundNativeDefault && !foundFallthroughDefault) {
2395*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("case default {}");
2396*c8dee2aaSAndroid Build Coastguard Worker     }
2397*c8dee2aaSAndroid Build Coastguard Worker 
2398*c8dee2aaSAndroid Build Coastguard Worker     --fIndentation;
2399*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("}");
2400*c8dee2aaSAndroid Build Coastguard Worker }
2401*c8dee2aaSAndroid Build Coastguard Worker 
writeVarDeclaration(const VarDeclaration & varDecl)2402*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl) {
2403*c8dee2aaSAndroid Build Coastguard Worker     std::string initialValue =
2404*c8dee2aaSAndroid Build Coastguard Worker             varDecl.value() ? this->assembleExpression(*varDecl.value(), Precedence::kAssignment)
2405*c8dee2aaSAndroid Build Coastguard Worker                             : std::string();
2406*c8dee2aaSAndroid Build Coastguard Worker 
2407*c8dee2aaSAndroid Build Coastguard Worker     if (varDecl.var()->modifierFlags().isConst() ||
2408*c8dee2aaSAndroid Build Coastguard Worker         (fProgram.fUsage->get(*varDecl.var()).fWrite == 1 && varDecl.value())) {
2409*c8dee2aaSAndroid Build Coastguard Worker         // Use `const` at global scope, or if the value is a compile-time constant.
2410*c8dee2aaSAndroid Build Coastguard Worker         SkASSERTF(varDecl.value(), "an immutable variable must specify a value");
2411*c8dee2aaSAndroid Build Coastguard Worker         const bool useConst =
2412*c8dee2aaSAndroid Build Coastguard Worker                 !fAtFunctionScope || Analysis::IsCompileTimeConstant(*varDecl.value());
2413*c8dee2aaSAndroid Build Coastguard Worker         this->write(useConst ? "const " : "let ");
2414*c8dee2aaSAndroid Build Coastguard Worker     } else {
2415*c8dee2aaSAndroid Build Coastguard Worker         this->write("var ");
2416*c8dee2aaSAndroid Build Coastguard Worker     }
2417*c8dee2aaSAndroid Build Coastguard Worker     this->write(this->assembleName(varDecl.var()->mangledName()));
2418*c8dee2aaSAndroid Build Coastguard Worker     this->write(": ");
2419*c8dee2aaSAndroid Build Coastguard Worker     this->write(to_wgsl_type(fContext, varDecl.var()->type(), &varDecl.var()->layout()));
2420*c8dee2aaSAndroid Build Coastguard Worker 
2421*c8dee2aaSAndroid Build Coastguard Worker     if (varDecl.value()) {
2422*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
2423*c8dee2aaSAndroid Build Coastguard Worker         this->write(initialValue);
2424*c8dee2aaSAndroid Build Coastguard Worker     }
2425*c8dee2aaSAndroid Build Coastguard Worker 
2426*c8dee2aaSAndroid Build Coastguard Worker     this->write(";");
2427*c8dee2aaSAndroid Build Coastguard Worker }
2428*c8dee2aaSAndroid Build Coastguard Worker 
makeLValue(const Expression & e)2429*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<WGSLCodeGenerator::LValue> WGSLCodeGenerator::makeLValue(const Expression& e) {
2430*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<VariableReference>()) {
2431*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<PointerLValue>(
2432*c8dee2aaSAndroid Build Coastguard Worker                 this->variableReferenceNameForLValue(e.as<VariableReference>()));
2433*c8dee2aaSAndroid Build Coastguard Worker     }
2434*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<FieldAccess>()) {
2435*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<PointerLValue>(this->assembleFieldAccess(e.as<FieldAccess>()));
2436*c8dee2aaSAndroid Build Coastguard Worker     }
2437*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<IndexExpression>()) {
2438*c8dee2aaSAndroid Build Coastguard Worker         const IndexExpression& idx = e.as<IndexExpression>();
2439*c8dee2aaSAndroid Build Coastguard Worker         if (idx.base()->type().isVector()) {
2440*c8dee2aaSAndroid Build Coastguard Worker             // Rewrite indexed-swizzle accesses like `myVec.zyx[i]` into an index onto `myVec`.
2441*c8dee2aaSAndroid Build Coastguard Worker             if (std::unique_ptr<Expression> rewrite =
2442*c8dee2aaSAndroid Build Coastguard Worker                         Transform::RewriteIndexedSwizzle(fContext, idx)) {
2443*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<VectorComponentLValue>(
2444*c8dee2aaSAndroid Build Coastguard Worker                         this->assembleExpression(*rewrite, Precedence::kAssignment));
2445*c8dee2aaSAndroid Build Coastguard Worker             } else {
2446*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<VectorComponentLValue>(this->assembleIndexExpression(idx));
2447*c8dee2aaSAndroid Build Coastguard Worker             }
2448*c8dee2aaSAndroid Build Coastguard Worker         } else {
2449*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<PointerLValue>(this->assembleIndexExpression(idx));
2450*c8dee2aaSAndroid Build Coastguard Worker         }
2451*c8dee2aaSAndroid Build Coastguard Worker     }
2452*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<Swizzle>()) {
2453*c8dee2aaSAndroid Build Coastguard Worker         const Swizzle& swizzle = e.as<Swizzle>();
2454*c8dee2aaSAndroid Build Coastguard Worker         if (swizzle.components().size() == 1) {
2455*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<VectorComponentLValue>(this->assembleSwizzle(swizzle));
2456*c8dee2aaSAndroid Build Coastguard Worker         } else {
2457*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<SwizzleLValue>(
2458*c8dee2aaSAndroid Build Coastguard Worker                     fContext,
2459*c8dee2aaSAndroid Build Coastguard Worker                     this->assembleExpression(*swizzle.base(), Precedence::kAssignment),
2460*c8dee2aaSAndroid Build Coastguard Worker                     swizzle.base()->type(),
2461*c8dee2aaSAndroid Build Coastguard Worker                     swizzle.components());
2462*c8dee2aaSAndroid Build Coastguard Worker         }
2463*c8dee2aaSAndroid Build Coastguard Worker     }
2464*c8dee2aaSAndroid Build Coastguard Worker 
2465*c8dee2aaSAndroid Build Coastguard Worker     fContext.fErrors->error(e.fPosition, "unsupported lvalue type");
2466*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
2467*c8dee2aaSAndroid Build Coastguard Worker }
2468*c8dee2aaSAndroid Build Coastguard Worker 
assembleExpression(const Expression & e,Precedence parentPrecedence)2469*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleExpression(const Expression& e,
2470*c8dee2aaSAndroid Build Coastguard Worker                                                   Precedence parentPrecedence) {
2471*c8dee2aaSAndroid Build Coastguard Worker     switch (e.kind()) {
2472*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kBinary:
2473*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryExpression(e.as<BinaryExpression>(), parentPrecedence);
2474*c8dee2aaSAndroid Build Coastguard Worker 
2475*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompound:
2476*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleConstructorCompound(e.as<ConstructorCompound>());
2477*c8dee2aaSAndroid Build Coastguard Worker 
2478*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArrayCast:
2479*c8dee2aaSAndroid Build Coastguard Worker             // This is a no-op, since WGSL 1.0 doesn't have any concept of precision qualifiers.
2480*c8dee2aaSAndroid Build Coastguard Worker             // When we add support for f16, this will need to copy the array contents.
2481*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleExpression(*e.as<ConstructorArrayCast>().argument(),
2482*c8dee2aaSAndroid Build Coastguard Worker                                             parentPrecedence);
2483*c8dee2aaSAndroid Build Coastguard Worker 
2484*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArray:
2485*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompoundCast:
2486*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorScalarCast:
2487*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorSplat:
2488*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorStruct:
2489*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleAnyConstructor(e.asAnyConstructor());
2490*c8dee2aaSAndroid Build Coastguard Worker 
2491*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorDiagonalMatrix:
2492*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
2493*c8dee2aaSAndroid Build Coastguard Worker 
2494*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorMatrixResize:
2495*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleConstructorMatrixResize(e.as<ConstructorMatrixResize>());
2496*c8dee2aaSAndroid Build Coastguard Worker 
2497*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kEmpty:
2498*c8dee2aaSAndroid Build Coastguard Worker             return "false";
2499*c8dee2aaSAndroid Build Coastguard Worker 
2500*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess:
2501*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleFieldAccess(e.as<FieldAccess>());
2502*c8dee2aaSAndroid Build Coastguard Worker 
2503*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFunctionCall:
2504*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleFunctionCall(e.as<FunctionCall>(), parentPrecedence);
2505*c8dee2aaSAndroid Build Coastguard Worker 
2506*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex:
2507*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleIndexExpression(e.as<IndexExpression>());
2508*c8dee2aaSAndroid Build Coastguard Worker 
2509*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kLiteral:
2510*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleLiteral(e.as<Literal>());
2511*c8dee2aaSAndroid Build Coastguard Worker 
2512*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPrefix:
2513*c8dee2aaSAndroid Build Coastguard Worker             return this->assemblePrefixExpression(e.as<PrefixExpression>(), parentPrecedence);
2514*c8dee2aaSAndroid Build Coastguard Worker 
2515*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPostfix:
2516*c8dee2aaSAndroid Build Coastguard Worker             return this->assemblePostfixExpression(e.as<PostfixExpression>(), parentPrecedence);
2517*c8dee2aaSAndroid Build Coastguard Worker 
2518*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSetting:
2519*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleExpression(*e.as<Setting>().toLiteral(fCaps), parentPrecedence);
2520*c8dee2aaSAndroid Build Coastguard Worker 
2521*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSwizzle:
2522*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSwizzle(e.as<Swizzle>());
2523*c8dee2aaSAndroid Build Coastguard Worker 
2524*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kTernary:
2525*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleTernaryExpression(e.as<TernaryExpression>(), parentPrecedence);
2526*c8dee2aaSAndroid Build Coastguard Worker 
2527*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference:
2528*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleVariableReference(e.as<VariableReference>());
2529*c8dee2aaSAndroid Build Coastguard Worker 
2530*c8dee2aaSAndroid Build Coastguard Worker         default:
2531*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported expression:\n%s", e.description().c_str());
2532*c8dee2aaSAndroid Build Coastguard Worker             return {};
2533*c8dee2aaSAndroid Build Coastguard Worker     }
2534*c8dee2aaSAndroid Build Coastguard Worker }
2535*c8dee2aaSAndroid Build Coastguard Worker 
is_nontrivial_expression(const Expression & expr)2536*c8dee2aaSAndroid Build Coastguard Worker static bool is_nontrivial_expression(const Expression& expr) {
2537*c8dee2aaSAndroid Build Coastguard Worker     // We consider a "trivial expression" one which we can repeat multiple times in the output
2538*c8dee2aaSAndroid Build Coastguard Worker     // without being dangerous or spammy. We avoid emitting temporary variables for very trivial
2539*c8dee2aaSAndroid Build Coastguard Worker     // expressions: literals, unadorned variable references, or constant vectors.
2540*c8dee2aaSAndroid Build Coastguard Worker     if (expr.is<VariableReference>() || expr.is<Literal>()) {
2541*c8dee2aaSAndroid Build Coastguard Worker         // Variables and literals are trivial; adding a let-declaration won't simplify anything.
2542*c8dee2aaSAndroid Build Coastguard Worker         return false;
2543*c8dee2aaSAndroid Build Coastguard Worker     }
2544*c8dee2aaSAndroid Build Coastguard Worker     if (expr.type().isVector() && Analysis::IsConstantExpression(expr)) {
2545*c8dee2aaSAndroid Build Coastguard Worker         // Compile-time constant vectors are also considered trivial; they're short and sweet.
2546*c8dee2aaSAndroid Build Coastguard Worker         return false;
2547*c8dee2aaSAndroid Build Coastguard Worker     }
2548*c8dee2aaSAndroid Build Coastguard Worker     return true;
2549*c8dee2aaSAndroid Build Coastguard Worker }
2550*c8dee2aaSAndroid Build Coastguard Worker 
binary_op_is_ambiguous_in_wgsl(Operator op)2551*c8dee2aaSAndroid Build Coastguard Worker static bool binary_op_is_ambiguous_in_wgsl(Operator op) {
2552*c8dee2aaSAndroid Build Coastguard Worker     // WGSL always requires parentheses for some operators which are deemed to be ambiguous.
2553*c8dee2aaSAndroid Build Coastguard Worker     // (8.19. Operator Precedence and Associativity)
2554*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2555*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALOR:
2556*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALAND:
2557*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEOR:
2558*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEAND:
2559*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEXOR:
2560*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SHL:
2561*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SHR:
2562*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LT:
2563*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GT:
2564*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LTEQ:
2565*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GTEQ:
2566*c8dee2aaSAndroid Build Coastguard Worker             return true;
2567*c8dee2aaSAndroid Build Coastguard Worker 
2568*c8dee2aaSAndroid Build Coastguard Worker         default:
2569*c8dee2aaSAndroid Build Coastguard Worker             return false;
2570*c8dee2aaSAndroid Build Coastguard Worker     }
2571*c8dee2aaSAndroid Build Coastguard Worker }
2572*c8dee2aaSAndroid Build Coastguard Worker 
binaryOpNeedsComponentwiseMatrixPolyfill(const Type & left,const Type & right,Operator op)2573*c8dee2aaSAndroid Build Coastguard Worker bool WGSLCodeGenerator::binaryOpNeedsComponentwiseMatrixPolyfill(const Type& left,
2574*c8dee2aaSAndroid Build Coastguard Worker                                                                  const Type& right,
2575*c8dee2aaSAndroid Build Coastguard Worker                                                                  Operator op) {
2576*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2577*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SLASH:
2578*c8dee2aaSAndroid Build Coastguard Worker             // WGSL does not natively support componentwise matrix-op-matrix for division.
2579*c8dee2aaSAndroid Build Coastguard Worker             if (left.isMatrix() && right.isMatrix()) {
2580*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2581*c8dee2aaSAndroid Build Coastguard Worker             }
2582*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
2583*c8dee2aaSAndroid Build Coastguard Worker 
2584*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::PLUS:
2585*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::MINUS:
2586*c8dee2aaSAndroid Build Coastguard Worker             // WGSL does not natively support componentwise matrix-op-scalar or scalar-op-matrix for
2587*c8dee2aaSAndroid Build Coastguard Worker             // addition, subtraction or division.
2588*c8dee2aaSAndroid Build Coastguard Worker             return (left.isMatrix() && right.isScalar()) ||
2589*c8dee2aaSAndroid Build Coastguard Worker                    (left.isScalar() && right.isMatrix());
2590*c8dee2aaSAndroid Build Coastguard Worker 
2591*c8dee2aaSAndroid Build Coastguard Worker         default:
2592*c8dee2aaSAndroid Build Coastguard Worker             return false;
2593*c8dee2aaSAndroid Build Coastguard Worker     }
2594*c8dee2aaSAndroid Build Coastguard Worker }
2595*c8dee2aaSAndroid Build Coastguard Worker 
assembleBinaryExpression(const BinaryExpression & b,Precedence parentPrecedence)2596*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleBinaryExpression(const BinaryExpression& b,
2597*c8dee2aaSAndroid Build Coastguard Worker                                                         Precedence parentPrecedence) {
2598*c8dee2aaSAndroid Build Coastguard Worker     return this->assembleBinaryExpression(*b.left(), b.getOperator(), *b.right(), b.type(),
2599*c8dee2aaSAndroid Build Coastguard Worker                                           parentPrecedence);
2600*c8dee2aaSAndroid Build Coastguard Worker }
2601*c8dee2aaSAndroid Build Coastguard Worker 
assembleBinaryExpression(const Expression & left,Operator op,const Expression & right,const Type & resultType,Precedence parentPrecedence)2602*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleBinaryExpression(const Expression& left,
2603*c8dee2aaSAndroid Build Coastguard Worker                                                         Operator op,
2604*c8dee2aaSAndroid Build Coastguard Worker                                                         const Expression& right,
2605*c8dee2aaSAndroid Build Coastguard Worker                                                         const Type& resultType,
2606*c8dee2aaSAndroid Build Coastguard Worker                                                         Precedence parentPrecedence) {
2607*c8dee2aaSAndroid Build Coastguard Worker     // If the operator is && or ||, we need to handle short-circuiting properly. Specifically, we
2608*c8dee2aaSAndroid Build Coastguard Worker     // sometimes need to emit extra statements to paper over functionality that WGSL lacks, like
2609*c8dee2aaSAndroid Build Coastguard Worker     // assignment in the middle of an expression. We need to guard those extra statements, to ensure
2610*c8dee2aaSAndroid Build Coastguard Worker     // that they don't occur if the expression evaluation is short-circuited. Converting the
2611*c8dee2aaSAndroid Build Coastguard Worker     // expression into an if-else block keeps the short-circuit property intact even when extra
2612*c8dee2aaSAndroid Build Coastguard Worker     // statements are involved.
2613*c8dee2aaSAndroid Build Coastguard Worker     // If the RHS doesn't have any side effects, then it's safe to just leave the expression as-is,
2614*c8dee2aaSAndroid Build Coastguard Worker     // since we know that any possible extra statements are non-side-effecting.
2615*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
2616*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == OperatorKind::LOGICALAND && Analysis::HasSideEffects(right)) {
2617*c8dee2aaSAndroid Build Coastguard Worker         // Converts `left_expression && right_expression` into the following block:
2618*c8dee2aaSAndroid Build Coastguard Worker 
2619*c8dee2aaSAndroid Build Coastguard Worker         // var _skTemp1: bool;
2620*c8dee2aaSAndroid Build Coastguard Worker         // [[ prepare left_expression ]]
2621*c8dee2aaSAndroid Build Coastguard Worker         // if left_expression {
2622*c8dee2aaSAndroid Build Coastguard Worker         //     [[ prepare right_expression ]]
2623*c8dee2aaSAndroid Build Coastguard Worker         //     _skTemp1 = right_expression;
2624*c8dee2aaSAndroid Build Coastguard Worker         // } else {
2625*c8dee2aaSAndroid Build Coastguard Worker         //     _skTemp1 = false;
2626*c8dee2aaSAndroid Build Coastguard Worker         // }
2627*c8dee2aaSAndroid Build Coastguard Worker 
2628*c8dee2aaSAndroid Build Coastguard Worker         expr = this->writeScratchVar(resultType);
2629*c8dee2aaSAndroid Build Coastguard Worker 
2630*c8dee2aaSAndroid Build Coastguard Worker         std::string leftExpr = this->assembleExpression(left, Precedence::kExpression);
2631*c8dee2aaSAndroid Build Coastguard Worker         this->write("if ");
2632*c8dee2aaSAndroid Build Coastguard Worker         this->write(leftExpr);
2633*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" {");
2634*c8dee2aaSAndroid Build Coastguard Worker 
2635*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
2636*c8dee2aaSAndroid Build Coastguard Worker         std::string rightExpr = this->assembleExpression(right, Precedence::kAssignment);
2637*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
2638*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
2639*c8dee2aaSAndroid Build Coastguard Worker         this->write(rightExpr);
2640*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
2641*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
2642*c8dee2aaSAndroid Build Coastguard Worker 
2643*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("} else {");
2644*c8dee2aaSAndroid Build Coastguard Worker 
2645*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
2646*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
2647*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" = false;");
2648*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
2649*c8dee2aaSAndroid Build Coastguard Worker 
2650*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
2651*c8dee2aaSAndroid Build Coastguard Worker         return expr;
2652*c8dee2aaSAndroid Build Coastguard Worker     }
2653*c8dee2aaSAndroid Build Coastguard Worker 
2654*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == OperatorKind::LOGICALOR && Analysis::HasSideEffects(right)) {
2655*c8dee2aaSAndroid Build Coastguard Worker         // Converts `left_expression || right_expression` into the following block:
2656*c8dee2aaSAndroid Build Coastguard Worker 
2657*c8dee2aaSAndroid Build Coastguard Worker         // var _skTemp1: bool;
2658*c8dee2aaSAndroid Build Coastguard Worker         // [[ prepare left_expression ]]
2659*c8dee2aaSAndroid Build Coastguard Worker         // if left_expression {
2660*c8dee2aaSAndroid Build Coastguard Worker         //     _skTemp1 = true;
2661*c8dee2aaSAndroid Build Coastguard Worker         // } else {
2662*c8dee2aaSAndroid Build Coastguard Worker         //     [[ prepare right_expression ]]
2663*c8dee2aaSAndroid Build Coastguard Worker         //     _skTemp1 = right_expression;
2664*c8dee2aaSAndroid Build Coastguard Worker         // }
2665*c8dee2aaSAndroid Build Coastguard Worker 
2666*c8dee2aaSAndroid Build Coastguard Worker         expr = this->writeScratchVar(resultType);
2667*c8dee2aaSAndroid Build Coastguard Worker 
2668*c8dee2aaSAndroid Build Coastguard Worker         std::string leftExpr = this->assembleExpression(left, Precedence::kExpression);
2669*c8dee2aaSAndroid Build Coastguard Worker         this->write("if ");
2670*c8dee2aaSAndroid Build Coastguard Worker         this->write(leftExpr);
2671*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" {");
2672*c8dee2aaSAndroid Build Coastguard Worker 
2673*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
2674*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
2675*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" = true;");
2676*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
2677*c8dee2aaSAndroid Build Coastguard Worker 
2678*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("} else {");
2679*c8dee2aaSAndroid Build Coastguard Worker 
2680*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
2681*c8dee2aaSAndroid Build Coastguard Worker         std::string rightExpr = this->assembleExpression(right, Precedence::kAssignment);
2682*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
2683*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
2684*c8dee2aaSAndroid Build Coastguard Worker         this->write(rightExpr);
2685*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
2686*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
2687*c8dee2aaSAndroid Build Coastguard Worker 
2688*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
2689*c8dee2aaSAndroid Build Coastguard Worker         return expr;
2690*c8dee2aaSAndroid Build Coastguard Worker     }
2691*c8dee2aaSAndroid Build Coastguard Worker 
2692*c8dee2aaSAndroid Build Coastguard Worker     // Handle comma-expressions.
2693*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == OperatorKind::COMMA) {
2694*c8dee2aaSAndroid Build Coastguard Worker         // The result from the left-expression is ignored, but its side effects must occur.
2695*c8dee2aaSAndroid Build Coastguard Worker         this->assembleExpression(left, Precedence::kStatement);
2696*c8dee2aaSAndroid Build Coastguard Worker 
2697*c8dee2aaSAndroid Build Coastguard Worker         // Evaluate the right side normally.
2698*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleExpression(right, parentPrecedence);
2699*c8dee2aaSAndroid Build Coastguard Worker     }
2700*c8dee2aaSAndroid Build Coastguard Worker 
2701*c8dee2aaSAndroid Build Coastguard Worker     // Handle assignment-expressions.
2702*c8dee2aaSAndroid Build Coastguard Worker     if (op.isAssignment()) {
2703*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<LValue> lvalue = this->makeLValue(left);
2704*c8dee2aaSAndroid Build Coastguard Worker         if (!lvalue) {
2705*c8dee2aaSAndroid Build Coastguard Worker             return "";
2706*c8dee2aaSAndroid Build Coastguard Worker         }
2707*c8dee2aaSAndroid Build Coastguard Worker 
2708*c8dee2aaSAndroid Build Coastguard Worker         if (op.kind() == OperatorKind::EQ) {
2709*c8dee2aaSAndroid Build Coastguard Worker             // Evaluate the right-hand side of simple assignment (`a = b` --> `b`).
2710*c8dee2aaSAndroid Build Coastguard Worker             expr = this->assembleExpression(right, Precedence::kAssignment);
2711*c8dee2aaSAndroid Build Coastguard Worker         } else {
2712*c8dee2aaSAndroid Build Coastguard Worker             // Evaluate the right-hand side of compound-assignment (`a += b` --> `a + b`).
2713*c8dee2aaSAndroid Build Coastguard Worker             op = op.removeAssignment();
2714*c8dee2aaSAndroid Build Coastguard Worker 
2715*c8dee2aaSAndroid Build Coastguard Worker             std::string lhs = lvalue->load();
2716*c8dee2aaSAndroid Build Coastguard Worker             std::string rhs = this->assembleExpression(right, op.getBinaryPrecedence());
2717*c8dee2aaSAndroid Build Coastguard Worker 
2718*c8dee2aaSAndroid Build Coastguard Worker             if (this->binaryOpNeedsComponentwiseMatrixPolyfill(left.type(), right.type(), op)) {
2719*c8dee2aaSAndroid Build Coastguard Worker                 if (is_nontrivial_expression(right)) {
2720*c8dee2aaSAndroid Build Coastguard Worker                     rhs = this->writeScratchLet(rhs);
2721*c8dee2aaSAndroid Build Coastguard Worker                 }
2722*c8dee2aaSAndroid Build Coastguard Worker 
2723*c8dee2aaSAndroid Build Coastguard Worker                 expr = this->assembleComponentwiseMatrixBinary(left.type(), right.type(),
2724*c8dee2aaSAndroid Build Coastguard Worker                                                                lhs, rhs, op);
2725*c8dee2aaSAndroid Build Coastguard Worker             } else {
2726*c8dee2aaSAndroid Build Coastguard Worker                 expr = lhs + operator_name(op) + rhs;
2727*c8dee2aaSAndroid Build Coastguard Worker             }
2728*c8dee2aaSAndroid Build Coastguard Worker         }
2729*c8dee2aaSAndroid Build Coastguard Worker 
2730*c8dee2aaSAndroid Build Coastguard Worker         // Emit the assignment statement (`a = a + b`).
2731*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(lvalue->store(expr));
2732*c8dee2aaSAndroid Build Coastguard Worker 
2733*c8dee2aaSAndroid Build Coastguard Worker         // Return the lvalue (`a`) as the result, since the value might be used by the caller.
2734*c8dee2aaSAndroid Build Coastguard Worker         return lvalue->load();
2735*c8dee2aaSAndroid Build Coastguard Worker     }
2736*c8dee2aaSAndroid Build Coastguard Worker 
2737*c8dee2aaSAndroid Build Coastguard Worker     if (op.isEquality()) {
2738*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleEqualityExpression(left, right, op, parentPrecedence);
2739*c8dee2aaSAndroid Build Coastguard Worker     }
2740*c8dee2aaSAndroid Build Coastguard Worker 
2741*c8dee2aaSAndroid Build Coastguard Worker     Precedence precedence = op.getBinaryPrecedence();
2742*c8dee2aaSAndroid Build Coastguard Worker     bool needParens = precedence >= parentPrecedence;
2743*c8dee2aaSAndroid Build Coastguard Worker     if (binary_op_is_ambiguous_in_wgsl(op)) {
2744*c8dee2aaSAndroid Build Coastguard Worker         precedence = Precedence::kParentheses;
2745*c8dee2aaSAndroid Build Coastguard Worker     }
2746*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2747*c8dee2aaSAndroid Build Coastguard Worker         expr = "(";
2748*c8dee2aaSAndroid Build Coastguard Worker     }
2749*c8dee2aaSAndroid Build Coastguard Worker 
2750*c8dee2aaSAndroid Build Coastguard Worker     // If we are emitting `constant + constant`, this generally indicates that the values could not
2751*c8dee2aaSAndroid Build Coastguard Worker     // be constant-folded. This happens when the values overflow or become nan. WGSL will refuse to
2752*c8dee2aaSAndroid Build Coastguard Worker     // compile such expressions, as WGSL 1.0 has no infinity/nan support. However, the WGSL
2753*c8dee2aaSAndroid Build Coastguard Worker     // compile-time check can be dodged by putting one side into a let-variable. This technically
2754*c8dee2aaSAndroid Build Coastguard Worker     // gives us an indeterminate result, but the vast majority of backends will just calculate an
2755*c8dee2aaSAndroid Build Coastguard Worker     // infinity or nan here, as we would expect. (skia:14385)
2756*c8dee2aaSAndroid Build Coastguard Worker     bool bothSidesConstant = ConstantFolder::GetConstantValueOrNull(left) &&
2757*c8dee2aaSAndroid Build Coastguard Worker                              ConstantFolder::GetConstantValueOrNull(right);
2758*c8dee2aaSAndroid Build Coastguard Worker 
2759*c8dee2aaSAndroid Build Coastguard Worker     std::string lhs = this->assembleExpression(left, precedence);
2760*c8dee2aaSAndroid Build Coastguard Worker     std::string rhs = this->assembleExpression(right, precedence);
2761*c8dee2aaSAndroid Build Coastguard Worker 
2762*c8dee2aaSAndroid Build Coastguard Worker     if (this->binaryOpNeedsComponentwiseMatrixPolyfill(left.type(), right.type(), op)) {
2763*c8dee2aaSAndroid Build Coastguard Worker         if (bothSidesConstant || is_nontrivial_expression(left)) {
2764*c8dee2aaSAndroid Build Coastguard Worker             lhs = this->writeScratchLet(lhs);
2765*c8dee2aaSAndroid Build Coastguard Worker         }
2766*c8dee2aaSAndroid Build Coastguard Worker         if (is_nontrivial_expression(right)) {
2767*c8dee2aaSAndroid Build Coastguard Worker             rhs = this->writeScratchLet(rhs);
2768*c8dee2aaSAndroid Build Coastguard Worker         }
2769*c8dee2aaSAndroid Build Coastguard Worker 
2770*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleComponentwiseMatrixBinary(left.type(), right.type(), lhs, rhs, op);
2771*c8dee2aaSAndroid Build Coastguard Worker     } else {
2772*c8dee2aaSAndroid Build Coastguard Worker         if (bothSidesConstant) {
2773*c8dee2aaSAndroid Build Coastguard Worker             lhs = this->writeScratchLet(lhs);
2774*c8dee2aaSAndroid Build Coastguard Worker         }
2775*c8dee2aaSAndroid Build Coastguard Worker 
2776*c8dee2aaSAndroid Build Coastguard Worker         expr += lhs + operator_name(op) + rhs;
2777*c8dee2aaSAndroid Build Coastguard Worker     }
2778*c8dee2aaSAndroid Build Coastguard Worker 
2779*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2780*c8dee2aaSAndroid Build Coastguard Worker         expr += ')';
2781*c8dee2aaSAndroid Build Coastguard Worker     }
2782*c8dee2aaSAndroid Build Coastguard Worker 
2783*c8dee2aaSAndroid Build Coastguard Worker     return expr;
2784*c8dee2aaSAndroid Build Coastguard Worker }
2785*c8dee2aaSAndroid Build Coastguard Worker 
assembleFieldAccess(const FieldAccess & f)2786*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleFieldAccess(const FieldAccess& f) {
2787*c8dee2aaSAndroid Build Coastguard Worker     const Field* field = &f.base()->type().fields()[f.fieldIndex()];
2788*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
2789*c8dee2aaSAndroid Build Coastguard Worker 
2790*c8dee2aaSAndroid Build Coastguard Worker     if (FieldPolyfillInfo* polyfillInfo = fFieldPolyfillMap.find(field)) {
2791*c8dee2aaSAndroid Build Coastguard Worker         // We found a matrix uniform. We are required to pass some matrix uniforms as array vectors,
2792*c8dee2aaSAndroid Build Coastguard Worker         // since the std140 layout for a matrix assumes 4-column vectors for each row, and WGSL
2793*c8dee2aaSAndroid Build Coastguard Worker         // tightly packs 2-column matrices. When emitting code, we replace the field-access
2794*c8dee2aaSAndroid Build Coastguard Worker         // expression with a global variable which holds an unpacked version of the uniform.
2795*c8dee2aaSAndroid Build Coastguard Worker         polyfillInfo->fWasAccessed = true;
2796*c8dee2aaSAndroid Build Coastguard Worker 
2797*c8dee2aaSAndroid Build Coastguard Worker         // The polyfill can either be based directly onto a uniform in an interface block, or it
2798*c8dee2aaSAndroid Build Coastguard Worker         // might be based on an index-expression onto a uniform if the interface block is arrayed.
2799*c8dee2aaSAndroid Build Coastguard Worker         const Expression* base = f.base().get();
2800*c8dee2aaSAndroid Build Coastguard Worker         const IndexExpression* indexExpr = nullptr;
2801*c8dee2aaSAndroid Build Coastguard Worker         if (base->is<IndexExpression>()) {
2802*c8dee2aaSAndroid Build Coastguard Worker             indexExpr = &base->as<IndexExpression>();
2803*c8dee2aaSAndroid Build Coastguard Worker             base = indexExpr->base().get();
2804*c8dee2aaSAndroid Build Coastguard Worker         }
2805*c8dee2aaSAndroid Build Coastguard Worker 
2806*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(base->is<VariableReference>());
2807*c8dee2aaSAndroid Build Coastguard Worker         expr = polyfillInfo->fReplacementName;
2808*c8dee2aaSAndroid Build Coastguard Worker 
2809*c8dee2aaSAndroid Build Coastguard Worker         // If we had an index expression, we must append the index.
2810*c8dee2aaSAndroid Build Coastguard Worker         if (indexExpr) {
2811*c8dee2aaSAndroid Build Coastguard Worker             expr += '[';
2812*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleExpression(*indexExpr->index(), Precedence::kSequence);
2813*c8dee2aaSAndroid Build Coastguard Worker             expr += ']';
2814*c8dee2aaSAndroid Build Coastguard Worker         }
2815*c8dee2aaSAndroid Build Coastguard Worker         return expr;
2816*c8dee2aaSAndroid Build Coastguard Worker     }
2817*c8dee2aaSAndroid Build Coastguard Worker 
2818*c8dee2aaSAndroid Build Coastguard Worker     switch (f.ownerKind()) {
2819*c8dee2aaSAndroid Build Coastguard Worker         case FieldAccess::OwnerKind::kDefault:
2820*c8dee2aaSAndroid Build Coastguard Worker             expr = this->assembleExpression(*f.base(), Precedence::kPostfix) + '.';
2821*c8dee2aaSAndroid Build Coastguard Worker             break;
2822*c8dee2aaSAndroid Build Coastguard Worker 
2823*c8dee2aaSAndroid Build Coastguard Worker         case FieldAccess::OwnerKind::kAnonymousInterfaceBlock:
2824*c8dee2aaSAndroid Build Coastguard Worker             if (f.base()->is<VariableReference>() &&
2825*c8dee2aaSAndroid Build Coastguard Worker                 field->fLayout.fBuiltin != SK_POINTSIZE_BUILTIN) {
2826*c8dee2aaSAndroid Build Coastguard Worker                 expr = this->variablePrefix(*f.base()->as<VariableReference>().variable());
2827*c8dee2aaSAndroid Build Coastguard Worker             }
2828*c8dee2aaSAndroid Build Coastguard Worker             break;
2829*c8dee2aaSAndroid Build Coastguard Worker     }
2830*c8dee2aaSAndroid Build Coastguard Worker 
2831*c8dee2aaSAndroid Build Coastguard Worker     expr += this->assembleName(field->fName);
2832*c8dee2aaSAndroid Build Coastguard Worker     return expr;
2833*c8dee2aaSAndroid Build Coastguard Worker }
2834*c8dee2aaSAndroid Build Coastguard Worker 
all_arguments_constant(const ExpressionArray & arguments)2835*c8dee2aaSAndroid Build Coastguard Worker static bool all_arguments_constant(const ExpressionArray& arguments) {
2836*c8dee2aaSAndroid Build Coastguard Worker     // Returns true if all arguments in the ExpressionArray are compile-time constants. If we are
2837*c8dee2aaSAndroid Build Coastguard Worker     // calling an intrinsic and all of its inputs are constant, but we didn't constant-fold it, this
2838*c8dee2aaSAndroid Build Coastguard Worker     // generally indicates that constant-folding resulted in an infinity or nan. The WGSL compiler
2839*c8dee2aaSAndroid Build Coastguard Worker     // will reject such an expression with a compile-time error. We can dodge the error, taking on
2840*c8dee2aaSAndroid Build Coastguard Worker     // the risk of indeterminate behavior instead, by replacing one of the constant values with a
2841*c8dee2aaSAndroid Build Coastguard Worker     // scratch let-variable. (skia:14385)
2842*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Expression>& arg : arguments) {
2843*c8dee2aaSAndroid Build Coastguard Worker         if (!ConstantFolder::GetConstantValueOrNull(*arg)) {
2844*c8dee2aaSAndroid Build Coastguard Worker             return false;
2845*c8dee2aaSAndroid Build Coastguard Worker         }
2846*c8dee2aaSAndroid Build Coastguard Worker     }
2847*c8dee2aaSAndroid Build Coastguard Worker     return true;
2848*c8dee2aaSAndroid Build Coastguard Worker }
2849*c8dee2aaSAndroid Build Coastguard Worker 
assembleSimpleIntrinsic(std::string_view intrinsicName,const FunctionCall & call)2850*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleSimpleIntrinsic(std::string_view intrinsicName,
2851*c8dee2aaSAndroid Build Coastguard Worker                                                        const FunctionCall& call) {
2852*c8dee2aaSAndroid Build Coastguard Worker     // Invoke the function, passing each function argument.
2853*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = std::string(intrinsicName);
2854*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back('(');
2855*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& args = call.arguments();
2856*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
2857*c8dee2aaSAndroid Build Coastguard Worker     bool allConstant = all_arguments_constant(call.arguments());
2858*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < args.size(); ++index) {
2859*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
2860*c8dee2aaSAndroid Build Coastguard Worker 
2861*c8dee2aaSAndroid Build Coastguard Worker         std::string argument = this->assembleExpression(*args[index], Precedence::kSequence);
2862*c8dee2aaSAndroid Build Coastguard Worker         if (args[index]->type().isAtomic()) {
2863*c8dee2aaSAndroid Build Coastguard Worker             // WGSL passes atomic values to intrinsics as pointers.
2864*c8dee2aaSAndroid Build Coastguard Worker             expr += '&';
2865*c8dee2aaSAndroid Build Coastguard Worker             expr += argument;
2866*c8dee2aaSAndroid Build Coastguard Worker         } else if (allConstant && index == 0) {
2867*c8dee2aaSAndroid Build Coastguard Worker             // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385)
2868*c8dee2aaSAndroid Build Coastguard Worker             expr += this->writeScratchLet(argument);
2869*c8dee2aaSAndroid Build Coastguard Worker         } else {
2870*c8dee2aaSAndroid Build Coastguard Worker             expr += argument;
2871*c8dee2aaSAndroid Build Coastguard Worker         }
2872*c8dee2aaSAndroid Build Coastguard Worker     }
2873*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back(')');
2874*c8dee2aaSAndroid Build Coastguard Worker 
2875*c8dee2aaSAndroid Build Coastguard Worker     if (call.type().isVoid()) {
2876*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
2877*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
2878*c8dee2aaSAndroid Build Coastguard Worker         return std::string();
2879*c8dee2aaSAndroid Build Coastguard Worker     } else {
2880*c8dee2aaSAndroid Build Coastguard Worker         return this->writeScratchLet(expr);
2881*c8dee2aaSAndroid Build Coastguard Worker     }
2882*c8dee2aaSAndroid Build Coastguard Worker }
2883*c8dee2aaSAndroid Build Coastguard Worker 
assembleVectorizedIntrinsic(std::string_view intrinsicName,const FunctionCall & call)2884*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleVectorizedIntrinsic(std::string_view intrinsicName,
2885*c8dee2aaSAndroid Build Coastguard Worker                                                            const FunctionCall& call) {
2886*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!call.type().isVoid());
2887*c8dee2aaSAndroid Build Coastguard Worker 
2888*c8dee2aaSAndroid Build Coastguard Worker     // Invoke the function, passing each function argument.
2889*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = std::string(intrinsicName);
2890*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back('(');
2891*c8dee2aaSAndroid Build Coastguard Worker 
2892*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
2893*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& args = call.arguments();
2894*c8dee2aaSAndroid Build Coastguard Worker     bool returnsVector = call.type().isVector();
2895*c8dee2aaSAndroid Build Coastguard Worker     bool allConstant = all_arguments_constant(call.arguments());
2896*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < args.size(); ++index) {
2897*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
2898*c8dee2aaSAndroid Build Coastguard Worker 
2899*c8dee2aaSAndroid Build Coastguard Worker         bool vectorize = returnsVector && args[index]->type().isScalar();
2900*c8dee2aaSAndroid Build Coastguard Worker         if (vectorize) {
2901*c8dee2aaSAndroid Build Coastguard Worker             expr += to_wgsl_type(fContext, call.type());
2902*c8dee2aaSAndroid Build Coastguard Worker             expr.push_back('(');
2903*c8dee2aaSAndroid Build Coastguard Worker         }
2904*c8dee2aaSAndroid Build Coastguard Worker 
2905*c8dee2aaSAndroid Build Coastguard Worker         // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385)
2906*c8dee2aaSAndroid Build Coastguard Worker         std::string argument = this->assembleExpression(*args[index], Precedence::kSequence);
2907*c8dee2aaSAndroid Build Coastguard Worker         expr += (allConstant && index == 0) ? this->writeScratchLet(argument)
2908*c8dee2aaSAndroid Build Coastguard Worker                                             : argument;
2909*c8dee2aaSAndroid Build Coastguard Worker         if (vectorize) {
2910*c8dee2aaSAndroid Build Coastguard Worker             expr.push_back(')');
2911*c8dee2aaSAndroid Build Coastguard Worker         }
2912*c8dee2aaSAndroid Build Coastguard Worker     }
2913*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back(')');
2914*c8dee2aaSAndroid Build Coastguard Worker 
2915*c8dee2aaSAndroid Build Coastguard Worker     return this->writeScratchLet(expr);
2916*c8dee2aaSAndroid Build Coastguard Worker }
2917*c8dee2aaSAndroid Build Coastguard Worker 
assembleUnaryOpIntrinsic(Operator op,const FunctionCall & call,Precedence parentPrecedence)2918*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleUnaryOpIntrinsic(Operator op,
2919*c8dee2aaSAndroid Build Coastguard Worker                                                         const FunctionCall& call,
2920*c8dee2aaSAndroid Build Coastguard Worker                                                         Precedence parentPrecedence) {
2921*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!call.type().isVoid());
2922*c8dee2aaSAndroid Build Coastguard Worker 
2923*c8dee2aaSAndroid Build Coastguard Worker     bool needParens = Precedence::kPrefix >= parentPrecedence;
2924*c8dee2aaSAndroid Build Coastguard Worker 
2925*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
2926*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2927*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back('(');
2928*c8dee2aaSAndroid Build Coastguard Worker     }
2929*c8dee2aaSAndroid Build Coastguard Worker 
2930*c8dee2aaSAndroid Build Coastguard Worker     expr += operator_name(op);
2931*c8dee2aaSAndroid Build Coastguard Worker     expr += this->assembleExpression(*call.arguments()[0], Precedence::kPrefix);
2932*c8dee2aaSAndroid Build Coastguard Worker 
2933*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2934*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back(')');
2935*c8dee2aaSAndroid Build Coastguard Worker     }
2936*c8dee2aaSAndroid Build Coastguard Worker 
2937*c8dee2aaSAndroid Build Coastguard Worker     return expr;
2938*c8dee2aaSAndroid Build Coastguard Worker }
2939*c8dee2aaSAndroid Build Coastguard Worker 
assembleBinaryOpIntrinsic(Operator op,const FunctionCall & call,Precedence parentPrecedence)2940*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleBinaryOpIntrinsic(Operator op,
2941*c8dee2aaSAndroid Build Coastguard Worker                                                          const FunctionCall& call,
2942*c8dee2aaSAndroid Build Coastguard Worker                                                          Precedence parentPrecedence) {
2943*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!call.type().isVoid());
2944*c8dee2aaSAndroid Build Coastguard Worker 
2945*c8dee2aaSAndroid Build Coastguard Worker     Precedence precedence = op.getBinaryPrecedence();
2946*c8dee2aaSAndroid Build Coastguard Worker     bool needParens = precedence >= parentPrecedence ||
2947*c8dee2aaSAndroid Build Coastguard Worker                       binary_op_is_ambiguous_in_wgsl(op);
2948*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
2949*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2950*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back('(');
2951*c8dee2aaSAndroid Build Coastguard Worker     }
2952*c8dee2aaSAndroid Build Coastguard Worker 
2953*c8dee2aaSAndroid Build Coastguard Worker     // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385)
2954*c8dee2aaSAndroid Build Coastguard Worker     std::string argument = this->assembleExpression(*call.arguments()[0], precedence);
2955*c8dee2aaSAndroid Build Coastguard Worker     expr += all_arguments_constant(call.arguments()) ? this->writeScratchLet(argument)
2956*c8dee2aaSAndroid Build Coastguard Worker                                                      : argument;
2957*c8dee2aaSAndroid Build Coastguard Worker     expr += operator_name(op);
2958*c8dee2aaSAndroid Build Coastguard Worker     expr += this->assembleExpression(*call.arguments()[1], precedence);
2959*c8dee2aaSAndroid Build Coastguard Worker 
2960*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
2961*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back(')');
2962*c8dee2aaSAndroid Build Coastguard Worker     }
2963*c8dee2aaSAndroid Build Coastguard Worker 
2964*c8dee2aaSAndroid Build Coastguard Worker     return expr;
2965*c8dee2aaSAndroid Build Coastguard Worker }
2966*c8dee2aaSAndroid Build Coastguard Worker 
2967*c8dee2aaSAndroid Build Coastguard Worker // Rewrite a WGSL intrinsic of the form "intrinsicName(in) -> struct" to the SkSL's
2968*c8dee2aaSAndroid Build Coastguard Worker // "intrinsicName(in, outField) -> returnField", where outField and returnField are the names of the
2969*c8dee2aaSAndroid Build Coastguard Worker // fields in the struct returned by the WGSL intrinsic.
assembleOutAssignedIntrinsic(std::string_view intrinsicName,std::string_view returnField,std::string_view outField,const FunctionCall & call)2970*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleOutAssignedIntrinsic(std::string_view intrinsicName,
2971*c8dee2aaSAndroid Build Coastguard Worker                                                             std::string_view returnField,
2972*c8dee2aaSAndroid Build Coastguard Worker                                                             std::string_view outField,
2973*c8dee2aaSAndroid Build Coastguard Worker                                                             const FunctionCall& call) {
2974*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(call.type().componentType().isNumber());
2975*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(call.arguments().size() == 2);
2976*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(call.function().parameters()[1]->modifierFlags() & ModifierFlag::kOut);
2977*c8dee2aaSAndroid Build Coastguard Worker 
2978*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = std::string(intrinsicName);
2979*c8dee2aaSAndroid Build Coastguard Worker     expr += "(";
2980*c8dee2aaSAndroid Build Coastguard Worker 
2981*c8dee2aaSAndroid Build Coastguard Worker     // Invoke the intrinsic with the first parameter. Use a scratch-let if argument is a constant
2982*c8dee2aaSAndroid Build Coastguard Worker     // to dodge WGSL overflow errors. (skia:14385)
2983*c8dee2aaSAndroid Build Coastguard Worker     std::string argument = this->assembleExpression(*call.arguments()[0], Precedence::kSequence);
2984*c8dee2aaSAndroid Build Coastguard Worker     expr += ConstantFolder::GetConstantValueOrNull(*call.arguments()[0])
2985*c8dee2aaSAndroid Build Coastguard Worker             ? this->writeScratchLet(argument) : argument;
2986*c8dee2aaSAndroid Build Coastguard Worker     expr += ")";
2987*c8dee2aaSAndroid Build Coastguard Worker     // In WGSL the intrinsic returns a struct; assign it to a local so that its fields can be
2988*c8dee2aaSAndroid Build Coastguard Worker     // accessed multiple times.
2989*c8dee2aaSAndroid Build Coastguard Worker     expr = this->writeScratchLet(expr);
2990*c8dee2aaSAndroid Build Coastguard Worker     expr += ".";
2991*c8dee2aaSAndroid Build Coastguard Worker 
2992*c8dee2aaSAndroid Build Coastguard Worker     // Store the outField of `expr` to the intended "out" argument
2993*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue = this->makeLValue(*call.arguments()[1]);
2994*c8dee2aaSAndroid Build Coastguard Worker     if (!lvalue) {
2995*c8dee2aaSAndroid Build Coastguard Worker         return "";
2996*c8dee2aaSAndroid Build Coastguard Worker     }
2997*c8dee2aaSAndroid Build Coastguard Worker     std::string outValue = expr;
2998*c8dee2aaSAndroid Build Coastguard Worker     outValue += outField;
2999*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(lvalue->store(outValue));
3000*c8dee2aaSAndroid Build Coastguard Worker 
3001*c8dee2aaSAndroid Build Coastguard Worker     // And return the expression accessing the returnField.
3002*c8dee2aaSAndroid Build Coastguard Worker     expr += returnField;
3003*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3004*c8dee2aaSAndroid Build Coastguard Worker }
3005*c8dee2aaSAndroid Build Coastguard Worker 
assemblePartialSampleCall(std::string_view functionName,const Expression & sampler,const Expression & coords)3006*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assemblePartialSampleCall(std::string_view functionName,
3007*c8dee2aaSAndroid Build Coastguard Worker                                                          const Expression& sampler,
3008*c8dee2aaSAndroid Build Coastguard Worker                                                          const Expression& coords) {
3009*c8dee2aaSAndroid Build Coastguard Worker     // This function returns `functionName(inSampler_texture, inSampler_sampler, coords` without a
3010*c8dee2aaSAndroid Build Coastguard Worker     // terminating comma or close-parenthesis. This allows the caller to add more arguments as
3011*c8dee2aaSAndroid Build Coastguard Worker     // needed.
3012*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(sampler.type().typeKind() == Type::TypeKind::kSampler);
3013*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = std::string(functionName) + '(';
3014*c8dee2aaSAndroid Build Coastguard Worker     expr += this->assembleExpression(sampler, Precedence::kSequence);
3015*c8dee2aaSAndroid Build Coastguard Worker     expr += kTextureSuffix;
3016*c8dee2aaSAndroid Build Coastguard Worker     expr += ", ";
3017*c8dee2aaSAndroid Build Coastguard Worker     expr += this->assembleExpression(sampler, Precedence::kSequence);
3018*c8dee2aaSAndroid Build Coastguard Worker     expr += kSamplerSuffix;
3019*c8dee2aaSAndroid Build Coastguard Worker     expr += ", ";
3020*c8dee2aaSAndroid Build Coastguard Worker 
3021*c8dee2aaSAndroid Build Coastguard Worker     // Compute the sample coordinates, dividing out the Z if a vec3 was provided.
3022*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(coords.type().isVector());
3023*c8dee2aaSAndroid Build Coastguard Worker     if (coords.type().columns() == 3) {
3024*c8dee2aaSAndroid Build Coastguard Worker         // The coordinates were passed as a vec3, so we need to emit `coords.xy / coords.z`.
3025*c8dee2aaSAndroid Build Coastguard Worker         std::string vec3Coords = this->writeScratchLet(coords, Precedence::kMultiplicative);
3026*c8dee2aaSAndroid Build Coastguard Worker         expr += vec3Coords + ".xy / " + vec3Coords + ".z";
3027*c8dee2aaSAndroid Build Coastguard Worker     } else {
3028*c8dee2aaSAndroid Build Coastguard Worker         // The coordinates should be a plain vec2; emit the expression as-is.
3029*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(coords.type().columns() == 2);
3030*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(coords, Precedence::kSequence);
3031*c8dee2aaSAndroid Build Coastguard Worker     }
3032*c8dee2aaSAndroid Build Coastguard Worker 
3033*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3034*c8dee2aaSAndroid Build Coastguard Worker }
3035*c8dee2aaSAndroid Build Coastguard Worker 
assembleComponentwiseMatrixBinary(const Type & leftType,const Type & rightType,const std::string & left,const std::string & right,Operator op)3036*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleComponentwiseMatrixBinary(const Type& leftType,
3037*c8dee2aaSAndroid Build Coastguard Worker                                                                  const Type& rightType,
3038*c8dee2aaSAndroid Build Coastguard Worker                                                                  const std::string& left,
3039*c8dee2aaSAndroid Build Coastguard Worker                                                                  const std::string& right,
3040*c8dee2aaSAndroid Build Coastguard Worker                                                                  Operator op) {
3041*c8dee2aaSAndroid Build Coastguard Worker     bool leftIsMatrix = leftType.isMatrix();
3042*c8dee2aaSAndroid Build Coastguard Worker     bool rightIsMatrix = rightType.isMatrix();
3043*c8dee2aaSAndroid Build Coastguard Worker     const Type& matrixType = leftIsMatrix ? leftType : rightType;
3044*c8dee2aaSAndroid Build Coastguard Worker 
3045*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, matrixType) + '(';
3046*c8dee2aaSAndroid Build Coastguard Worker     auto separator = String::Separator();
3047*c8dee2aaSAndroid Build Coastguard Worker     int columns = matrixType.columns();
3048*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < columns; ++c) {
3049*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
3050*c8dee2aaSAndroid Build Coastguard Worker         expr += left;
3051*c8dee2aaSAndroid Build Coastguard Worker         if (leftIsMatrix) {
3052*c8dee2aaSAndroid Build Coastguard Worker             expr += '[';
3053*c8dee2aaSAndroid Build Coastguard Worker             expr += std::to_string(c);
3054*c8dee2aaSAndroid Build Coastguard Worker             expr += ']';
3055*c8dee2aaSAndroid Build Coastguard Worker         }
3056*c8dee2aaSAndroid Build Coastguard Worker         expr += op.operatorName();
3057*c8dee2aaSAndroid Build Coastguard Worker         expr += right;
3058*c8dee2aaSAndroid Build Coastguard Worker         if (rightIsMatrix) {
3059*c8dee2aaSAndroid Build Coastguard Worker             expr += '[';
3060*c8dee2aaSAndroid Build Coastguard Worker             expr += std::to_string(c);
3061*c8dee2aaSAndroid Build Coastguard Worker             expr += ']';
3062*c8dee2aaSAndroid Build Coastguard Worker         }
3063*c8dee2aaSAndroid Build Coastguard Worker     }
3064*c8dee2aaSAndroid Build Coastguard Worker     return expr + ')';
3065*c8dee2aaSAndroid Build Coastguard Worker }
3066*c8dee2aaSAndroid Build Coastguard Worker 
assembleIntrinsicCall(const FunctionCall & call,IntrinsicKind kind,Precedence parentPrecedence)3067*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleIntrinsicCall(const FunctionCall& call,
3068*c8dee2aaSAndroid Build Coastguard Worker                                                      IntrinsicKind kind,
3069*c8dee2aaSAndroid Build Coastguard Worker                                                      Precedence parentPrecedence) {
3070*c8dee2aaSAndroid Build Coastguard Worker     // Be careful: WGSL 1.0 will reject any intrinsic calls which can be constant-evaluated to
3071*c8dee2aaSAndroid Build Coastguard Worker     // infinity or nan with a compile error. If all arguments to an intrinsic are compile-time
3072*c8dee2aaSAndroid Build Coastguard Worker     // constants (`all_arguments_constant`), it is safest to copy one argument into a scratch-let so
3073*c8dee2aaSAndroid Build Coastguard Worker     // that the call will be seen as runtime-evaluated, which defuses the overflow checks.
3074*c8dee2aaSAndroid Build Coastguard Worker     // Don't worry; a competent driver should still optimize it away.
3075*c8dee2aaSAndroid Build Coastguard Worker 
3076*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = call.arguments();
3077*c8dee2aaSAndroid Build Coastguard Worker     switch (kind) {
3078*c8dee2aaSAndroid Build Coastguard Worker         case k_atan_IntrinsicKind: {
3079*c8dee2aaSAndroid Build Coastguard Worker             const char* name = (arguments.size() == 1) ? "atan" : "atan2";
3080*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic(name, call);
3081*c8dee2aaSAndroid Build Coastguard Worker         }
3082*c8dee2aaSAndroid Build Coastguard Worker         case k_dFdx_IntrinsicKind:
3083*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("dpdx", call);
3084*c8dee2aaSAndroid Build Coastguard Worker 
3085*c8dee2aaSAndroid Build Coastguard Worker         case k_dFdy_IntrinsicKind:
3086*c8dee2aaSAndroid Build Coastguard Worker             // TODO(b/294274678): apply RTFlip here
3087*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("dpdy", call);
3088*c8dee2aaSAndroid Build Coastguard Worker 
3089*c8dee2aaSAndroid Build Coastguard Worker         case k_dot_IntrinsicKind: {
3090*c8dee2aaSAndroid Build Coastguard Worker             if (arguments[0]->type().isScalar()) {
3091*c8dee2aaSAndroid Build Coastguard Worker                 return this->assembleBinaryOpIntrinsic(OperatorKind::STAR, call, parentPrecedence);
3092*c8dee2aaSAndroid Build Coastguard Worker             }
3093*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("dot", call);
3094*c8dee2aaSAndroid Build Coastguard Worker         }
3095*c8dee2aaSAndroid Build Coastguard Worker         case k_equal_IntrinsicKind:
3096*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::EQEQ, call, parentPrecedence);
3097*c8dee2aaSAndroid Build Coastguard Worker 
3098*c8dee2aaSAndroid Build Coastguard Worker         case k_faceforward_IntrinsicKind: {
3099*c8dee2aaSAndroid Build Coastguard Worker             if (arguments[0]->type().isScalar()) {
3100*c8dee2aaSAndroid Build Coastguard Worker                 // select(-N, N, (I * Nref) < 0)
3101*c8dee2aaSAndroid Build Coastguard Worker                 std::string N = this->writeNontrivialScratchLet(*arguments[0],
3102*c8dee2aaSAndroid Build Coastguard Worker                                                                 Precedence::kAssignment);
3103*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeScratchLet(
3104*c8dee2aaSAndroid Build Coastguard Worker                         "select(-" + N + ", " + N + ", " +
3105*c8dee2aaSAndroid Build Coastguard Worker                         this->assembleBinaryExpression(*arguments[1],
3106*c8dee2aaSAndroid Build Coastguard Worker                                                        OperatorKind::STAR,
3107*c8dee2aaSAndroid Build Coastguard Worker                                                        *arguments[2],
3108*c8dee2aaSAndroid Build Coastguard Worker                                                        arguments[1]->type(),
3109*c8dee2aaSAndroid Build Coastguard Worker                                                        Precedence::kRelational) +
3110*c8dee2aaSAndroid Build Coastguard Worker                         " < 0)");
3111*c8dee2aaSAndroid Build Coastguard Worker             }
3112*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("faceForward", call);
3113*c8dee2aaSAndroid Build Coastguard Worker         }
3114*c8dee2aaSAndroid Build Coastguard Worker         case k_frexp_IntrinsicKind:
3115*c8dee2aaSAndroid Build Coastguard Worker             // SkSL frexp is "$genType fract = frexp($genType, out $genIType exp)" whereas WGSL
3116*c8dee2aaSAndroid Build Coastguard Worker             // returns a struct with no out param: "let [fract, exp] = frexp($genType)".
3117*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleOutAssignedIntrinsic("frexp", "fract", "exp", call);
3118*c8dee2aaSAndroid Build Coastguard Worker 
3119*c8dee2aaSAndroid Build Coastguard Worker         case k_greaterThan_IntrinsicKind:
3120*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::GT, call, parentPrecedence);
3121*c8dee2aaSAndroid Build Coastguard Worker 
3122*c8dee2aaSAndroid Build Coastguard Worker         case k_greaterThanEqual_IntrinsicKind:
3123*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::GTEQ, call, parentPrecedence);
3124*c8dee2aaSAndroid Build Coastguard Worker 
3125*c8dee2aaSAndroid Build Coastguard Worker         case k_inverse_IntrinsicKind:
3126*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleInversePolyfill(call);
3127*c8dee2aaSAndroid Build Coastguard Worker 
3128*c8dee2aaSAndroid Build Coastguard Worker         case k_inversesqrt_IntrinsicKind:
3129*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("inverseSqrt", call);
3130*c8dee2aaSAndroid Build Coastguard Worker 
3131*c8dee2aaSAndroid Build Coastguard Worker         case k_lessThan_IntrinsicKind:
3132*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::LT, call, parentPrecedence);
3133*c8dee2aaSAndroid Build Coastguard Worker 
3134*c8dee2aaSAndroid Build Coastguard Worker         case k_lessThanEqual_IntrinsicKind:
3135*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::LTEQ, call, parentPrecedence);
3136*c8dee2aaSAndroid Build Coastguard Worker 
3137*c8dee2aaSAndroid Build Coastguard Worker         case k_matrixCompMult_IntrinsicKind: {
3138*c8dee2aaSAndroid Build Coastguard Worker             // We use a scratch-let for arg0 to avoid the potential for WGSL overflow. (skia:14385)
3139*c8dee2aaSAndroid Build Coastguard Worker             std::string arg0 = all_arguments_constant(arguments)
3140*c8dee2aaSAndroid Build Coastguard Worker                             ? this->writeScratchLet(*arguments[0], Precedence::kPostfix)
3141*c8dee2aaSAndroid Build Coastguard Worker                             : this->writeNontrivialScratchLet(*arguments[0], Precedence::kPostfix);
3142*c8dee2aaSAndroid Build Coastguard Worker             std::string arg1 = this->writeNontrivialScratchLet(*arguments[1], Precedence::kPostfix);
3143*c8dee2aaSAndroid Build Coastguard Worker             return this->writeScratchLet(
3144*c8dee2aaSAndroid Build Coastguard Worker                     this->assembleComponentwiseMatrixBinary(arguments[0]->type(),
3145*c8dee2aaSAndroid Build Coastguard Worker                                                             arguments[1]->type(),
3146*c8dee2aaSAndroid Build Coastguard Worker                                                             arg0,
3147*c8dee2aaSAndroid Build Coastguard Worker                                                             arg1,
3148*c8dee2aaSAndroid Build Coastguard Worker                                                             OperatorKind::STAR));
3149*c8dee2aaSAndroid Build Coastguard Worker         }
3150*c8dee2aaSAndroid Build Coastguard Worker         case k_mix_IntrinsicKind: {
3151*c8dee2aaSAndroid Build Coastguard Worker             const char* name = arguments[2]->type().componentType().isBoolean() ? "select" : "mix";
3152*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleVectorizedIntrinsic(name, call);
3153*c8dee2aaSAndroid Build Coastguard Worker         }
3154*c8dee2aaSAndroid Build Coastguard Worker         case k_mod_IntrinsicKind: {
3155*c8dee2aaSAndroid Build Coastguard Worker             // WGSL has no intrinsic equivalent to `mod`. Synthesize `x - y * floor(x / y)`.
3156*c8dee2aaSAndroid Build Coastguard Worker             // We can use a scratch-let on one side to dodge WGSL overflow errors.  In practice, I
3157*c8dee2aaSAndroid Build Coastguard Worker             // can't find any values of x or y which would overflow, but it can't hurt. (skia:14385)
3158*c8dee2aaSAndroid Build Coastguard Worker             std::string arg0 = all_arguments_constant(arguments)
3159*c8dee2aaSAndroid Build Coastguard Worker                             ? this->writeScratchLet(*arguments[0], Precedence::kAdditive)
3160*c8dee2aaSAndroid Build Coastguard Worker                             : this->writeNontrivialScratchLet(*arguments[0], Precedence::kAdditive);
3161*c8dee2aaSAndroid Build Coastguard Worker             std::string arg1 = this->writeNontrivialScratchLet(*arguments[1],
3162*c8dee2aaSAndroid Build Coastguard Worker                                                                Precedence::kAdditive);
3163*c8dee2aaSAndroid Build Coastguard Worker             return this->writeScratchLet(arg0 + " - " + arg1 + " * floor(" +
3164*c8dee2aaSAndroid Build Coastguard Worker                                          arg0 + " / " + arg1 + ")");
3165*c8dee2aaSAndroid Build Coastguard Worker         }
3166*c8dee2aaSAndroid Build Coastguard Worker 
3167*c8dee2aaSAndroid Build Coastguard Worker         case k_modf_IntrinsicKind:
3168*c8dee2aaSAndroid Build Coastguard Worker             // SkSL modf is "$genType fract = modf($genType, out $genType whole)" whereas WGSL
3169*c8dee2aaSAndroid Build Coastguard Worker             // returns a struct with no out param: "let [fract, whole] = modf($genType)".
3170*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleOutAssignedIntrinsic("modf", "fract", "whole", call);
3171*c8dee2aaSAndroid Build Coastguard Worker 
3172*c8dee2aaSAndroid Build Coastguard Worker         case k_normalize_IntrinsicKind: {
3173*c8dee2aaSAndroid Build Coastguard Worker             const char* name = arguments[0]->type().isScalar() ? "sign" : "normalize";
3174*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic(name, call);
3175*c8dee2aaSAndroid Build Coastguard Worker         }
3176*c8dee2aaSAndroid Build Coastguard Worker         case k_not_IntrinsicKind:
3177*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleUnaryOpIntrinsic(OperatorKind::LOGICALNOT, call, parentPrecedence);
3178*c8dee2aaSAndroid Build Coastguard Worker 
3179*c8dee2aaSAndroid Build Coastguard Worker         case k_notEqual_IntrinsicKind:
3180*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleBinaryOpIntrinsic(OperatorKind::NEQ, call, parentPrecedence);
3181*c8dee2aaSAndroid Build Coastguard Worker 
3182*c8dee2aaSAndroid Build Coastguard Worker         case k_packHalf2x16_IntrinsicKind:
3183*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("pack2x16float", call);
3184*c8dee2aaSAndroid Build Coastguard Worker 
3185*c8dee2aaSAndroid Build Coastguard Worker         case k_packSnorm2x16_IntrinsicKind:
3186*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("pack2x16snorm", call);
3187*c8dee2aaSAndroid Build Coastguard Worker 
3188*c8dee2aaSAndroid Build Coastguard Worker         case k_packSnorm4x8_IntrinsicKind:
3189*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("pack4x8snorm", call);
3190*c8dee2aaSAndroid Build Coastguard Worker 
3191*c8dee2aaSAndroid Build Coastguard Worker         case k_packUnorm2x16_IntrinsicKind:
3192*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("pack2x16unorm", call);
3193*c8dee2aaSAndroid Build Coastguard Worker 
3194*c8dee2aaSAndroid Build Coastguard Worker         case k_packUnorm4x8_IntrinsicKind:
3195*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("pack4x8unorm", call);
3196*c8dee2aaSAndroid Build Coastguard Worker 
3197*c8dee2aaSAndroid Build Coastguard Worker         case k_reflect_IntrinsicKind:
3198*c8dee2aaSAndroid Build Coastguard Worker             if (arguments[0]->type().isScalar()) {
3199*c8dee2aaSAndroid Build Coastguard Worker                 // I - 2 * N * I * N
3200*c8dee2aaSAndroid Build Coastguard Worker                 // We can use a scratch-let for N to dodge WGSL overflow errors. (skia:14385)
3201*c8dee2aaSAndroid Build Coastguard Worker                 std::string I = this->writeNontrivialScratchLet(*arguments[0],
3202*c8dee2aaSAndroid Build Coastguard Worker                                                                 Precedence::kAdditive);
3203*c8dee2aaSAndroid Build Coastguard Worker                 std::string N = all_arguments_constant(arguments)
3204*c8dee2aaSAndroid Build Coastguard Worker                       ? this->writeScratchLet(*arguments[1], Precedence::kMultiplicative)
3205*c8dee2aaSAndroid Build Coastguard Worker                       : this->writeNontrivialScratchLet(*arguments[1], Precedence::kMultiplicative);
3206*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeScratchLet(String::printf("%s - 2 * %s * %s * %s",
3207*c8dee2aaSAndroid Build Coastguard Worker                                                             I.c_str(), N.c_str(),
3208*c8dee2aaSAndroid Build Coastguard Worker                                                             I.c_str(), N.c_str()));
3209*c8dee2aaSAndroid Build Coastguard Worker             }
3210*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("reflect", call);
3211*c8dee2aaSAndroid Build Coastguard Worker 
3212*c8dee2aaSAndroid Build Coastguard Worker         case k_refract_IntrinsicKind:
3213*c8dee2aaSAndroid Build Coastguard Worker             if (arguments[0]->type().isScalar()) {
3214*c8dee2aaSAndroid Build Coastguard Worker                 // WGSL only implements refract for vectors; rather than reimplementing refract from
3215*c8dee2aaSAndroid Build Coastguard Worker                 // scratch, we can replace the call with `refract(float2(I,0), float2(N,0), eta).x`.
3216*c8dee2aaSAndroid Build Coastguard Worker                 std::string I = this->writeNontrivialScratchLet(*arguments[0],
3217*c8dee2aaSAndroid Build Coastguard Worker                                                                 Precedence::kSequence);
3218*c8dee2aaSAndroid Build Coastguard Worker                 std::string N = this->writeNontrivialScratchLet(*arguments[1],
3219*c8dee2aaSAndroid Build Coastguard Worker                                                                 Precedence::kSequence);
3220*c8dee2aaSAndroid Build Coastguard Worker                 // We can use a scratch-let for Eta to avoid WGSL overflow errors. (skia:14385)
3221*c8dee2aaSAndroid Build Coastguard Worker                 std::string Eta = all_arguments_constant(arguments)
3222*c8dee2aaSAndroid Build Coastguard Worker                       ? this->writeScratchLet(*arguments[2], Precedence::kSequence)
3223*c8dee2aaSAndroid Build Coastguard Worker                       : this->writeNontrivialScratchLet(*arguments[2], Precedence::kSequence);
3224*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeScratchLet(
3225*c8dee2aaSAndroid Build Coastguard Worker                         String::printf("refract(vec2<%s>(%s, 0), vec2<%s>(%s, 0), %s).x",
3226*c8dee2aaSAndroid Build Coastguard Worker                                        to_wgsl_type(fContext, arguments[0]->type()).c_str(),
3227*c8dee2aaSAndroid Build Coastguard Worker                                        I.c_str(),
3228*c8dee2aaSAndroid Build Coastguard Worker                                        to_wgsl_type(fContext, arguments[1]->type()).c_str(),
3229*c8dee2aaSAndroid Build Coastguard Worker                                        N.c_str(),
3230*c8dee2aaSAndroid Build Coastguard Worker                                        Eta.c_str()));
3231*c8dee2aaSAndroid Build Coastguard Worker             }
3232*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("refract", call);
3233*c8dee2aaSAndroid Build Coastguard Worker 
3234*c8dee2aaSAndroid Build Coastguard Worker         case k_sample_IntrinsicKind: {
3235*c8dee2aaSAndroid Build Coastguard Worker             // Determine if a bias argument was passed in.
3236*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 2 || arguments.size() == 3);
3237*c8dee2aaSAndroid Build Coastguard Worker             bool callIncludesBias = (arguments.size() == 3);
3238*c8dee2aaSAndroid Build Coastguard Worker 
3239*c8dee2aaSAndroid Build Coastguard Worker             if (fProgram.fConfig->fSettings.fSharpenTextures || callIncludesBias) {
3240*c8dee2aaSAndroid Build Coastguard Worker                 // We need to supply a bias argument; this is a separate intrinsic in WGSL.
3241*c8dee2aaSAndroid Build Coastguard Worker                 std::string expr = this->assemblePartialSampleCall("textureSampleBias",
3242*c8dee2aaSAndroid Build Coastguard Worker                                                                    *arguments[0],
3243*c8dee2aaSAndroid Build Coastguard Worker                                                                    *arguments[1]);
3244*c8dee2aaSAndroid Build Coastguard Worker                 expr += ", ";
3245*c8dee2aaSAndroid Build Coastguard Worker                 if (callIncludesBias) {
3246*c8dee2aaSAndroid Build Coastguard Worker                     expr += this->assembleExpression(*arguments[2], Precedence::kAdditive) +
3247*c8dee2aaSAndroid Build Coastguard Worker                             " + ";
3248*c8dee2aaSAndroid Build Coastguard Worker                 }
3249*c8dee2aaSAndroid Build Coastguard Worker                 expr += skstd::to_string(fProgram.fConfig->fSettings.fSharpenTextures
3250*c8dee2aaSAndroid Build Coastguard Worker                                                  ? kSharpenTexturesBias
3251*c8dee2aaSAndroid Build Coastguard Worker                                                  : 0.0f);
3252*c8dee2aaSAndroid Build Coastguard Worker                 return expr + ')';
3253*c8dee2aaSAndroid Build Coastguard Worker             }
3254*c8dee2aaSAndroid Build Coastguard Worker 
3255*c8dee2aaSAndroid Build Coastguard Worker             // No bias is necessary, so we can call `textureSample` directly.
3256*c8dee2aaSAndroid Build Coastguard Worker             return this->assemblePartialSampleCall("textureSample",
3257*c8dee2aaSAndroid Build Coastguard Worker                                                    *arguments[0],
3258*c8dee2aaSAndroid Build Coastguard Worker                                                    *arguments[1]) + ')';
3259*c8dee2aaSAndroid Build Coastguard Worker         }
3260*c8dee2aaSAndroid Build Coastguard Worker         case k_sampleLod_IntrinsicKind: {
3261*c8dee2aaSAndroid Build Coastguard Worker             std::string expr = this->assemblePartialSampleCall("textureSampleLevel",
3262*c8dee2aaSAndroid Build Coastguard Worker                                                                *arguments[0],
3263*c8dee2aaSAndroid Build Coastguard Worker                                                                *arguments[1]);
3264*c8dee2aaSAndroid Build Coastguard Worker             expr += ", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3265*c8dee2aaSAndroid Build Coastguard Worker             return expr + ')';
3266*c8dee2aaSAndroid Build Coastguard Worker         }
3267*c8dee2aaSAndroid Build Coastguard Worker         case k_sampleGrad_IntrinsicKind: {
3268*c8dee2aaSAndroid Build Coastguard Worker             std::string expr = this->assemblePartialSampleCall("textureSampleGrad",
3269*c8dee2aaSAndroid Build Coastguard Worker                                                                *arguments[0],
3270*c8dee2aaSAndroid Build Coastguard Worker                                                                *arguments[1]);
3271*c8dee2aaSAndroid Build Coastguard Worker             expr += ", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3272*c8dee2aaSAndroid Build Coastguard Worker             expr += ", " + this->assembleExpression(*arguments[3], Precedence::kSequence);
3273*c8dee2aaSAndroid Build Coastguard Worker             return expr + ')';
3274*c8dee2aaSAndroid Build Coastguard Worker         }
3275*c8dee2aaSAndroid Build Coastguard Worker         case k_textureHeight_IntrinsicKind:
3276*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("textureDimensions", call) + ".y";
3277*c8dee2aaSAndroid Build Coastguard Worker 
3278*c8dee2aaSAndroid Build Coastguard Worker         case k_textureRead_IntrinsicKind: {
3279*c8dee2aaSAndroid Build Coastguard Worker             // We need to inject an extra argument for the mip-level. We don't plan on using mipmaps
3280*c8dee2aaSAndroid Build Coastguard Worker             // in our storage textures, so we can just pass zero.
3281*c8dee2aaSAndroid Build Coastguard Worker             std::string tex = this->assembleExpression(*arguments[0], Precedence::kSequence);
3282*c8dee2aaSAndroid Build Coastguard Worker             std::string pos = this->writeScratchLet(*arguments[1], Precedence::kSequence);
3283*c8dee2aaSAndroid Build Coastguard Worker             return std::string("textureLoad(") + tex + ", " + pos + ", 0)";
3284*c8dee2aaSAndroid Build Coastguard Worker         }
3285*c8dee2aaSAndroid Build Coastguard Worker         case k_textureWidth_IntrinsicKind:
3286*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("textureDimensions", call) + ".x";
3287*c8dee2aaSAndroid Build Coastguard Worker 
3288*c8dee2aaSAndroid Build Coastguard Worker         case k_textureWrite_IntrinsicKind:
3289*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("textureStore", call);
3290*c8dee2aaSAndroid Build Coastguard Worker 
3291*c8dee2aaSAndroid Build Coastguard Worker         case k_unpackHalf2x16_IntrinsicKind:
3292*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("unpack2x16float", call);
3293*c8dee2aaSAndroid Build Coastguard Worker 
3294*c8dee2aaSAndroid Build Coastguard Worker         case k_unpackSnorm2x16_IntrinsicKind:
3295*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("unpack2x16snorm", call);
3296*c8dee2aaSAndroid Build Coastguard Worker 
3297*c8dee2aaSAndroid Build Coastguard Worker         case k_unpackSnorm4x8_IntrinsicKind:
3298*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("unpack4x8snorm", call);
3299*c8dee2aaSAndroid Build Coastguard Worker 
3300*c8dee2aaSAndroid Build Coastguard Worker         case k_unpackUnorm2x16_IntrinsicKind:
3301*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("unpack2x16unorm", call);
3302*c8dee2aaSAndroid Build Coastguard Worker 
3303*c8dee2aaSAndroid Build Coastguard Worker         case k_unpackUnorm4x8_IntrinsicKind:
3304*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("unpack4x8unorm", call);
3305*c8dee2aaSAndroid Build Coastguard Worker 
3306*c8dee2aaSAndroid Build Coastguard Worker         case k_clamp_IntrinsicKind:
3307*c8dee2aaSAndroid Build Coastguard Worker         case k_max_IntrinsicKind:
3308*c8dee2aaSAndroid Build Coastguard Worker         case k_min_IntrinsicKind:
3309*c8dee2aaSAndroid Build Coastguard Worker         case k_smoothstep_IntrinsicKind:
3310*c8dee2aaSAndroid Build Coastguard Worker         case k_step_IntrinsicKind:
3311*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleVectorizedIntrinsic(call.function().name(), call);
3312*c8dee2aaSAndroid Build Coastguard Worker 
3313*c8dee2aaSAndroid Build Coastguard Worker         case k_abs_IntrinsicKind:
3314*c8dee2aaSAndroid Build Coastguard Worker         case k_acos_IntrinsicKind:
3315*c8dee2aaSAndroid Build Coastguard Worker         case k_all_IntrinsicKind:
3316*c8dee2aaSAndroid Build Coastguard Worker         case k_any_IntrinsicKind:
3317*c8dee2aaSAndroid Build Coastguard Worker         case k_asin_IntrinsicKind:
3318*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicAdd_IntrinsicKind:
3319*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicLoad_IntrinsicKind:
3320*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicStore_IntrinsicKind:
3321*c8dee2aaSAndroid Build Coastguard Worker         case k_ceil_IntrinsicKind:
3322*c8dee2aaSAndroid Build Coastguard Worker         case k_cos_IntrinsicKind:
3323*c8dee2aaSAndroid Build Coastguard Worker         case k_cross_IntrinsicKind:
3324*c8dee2aaSAndroid Build Coastguard Worker         case k_degrees_IntrinsicKind:
3325*c8dee2aaSAndroid Build Coastguard Worker         case k_distance_IntrinsicKind:
3326*c8dee2aaSAndroid Build Coastguard Worker         case k_exp_IntrinsicKind:
3327*c8dee2aaSAndroid Build Coastguard Worker         case k_exp2_IntrinsicKind:
3328*c8dee2aaSAndroid Build Coastguard Worker         case k_floor_IntrinsicKind:
3329*c8dee2aaSAndroid Build Coastguard Worker         case k_fract_IntrinsicKind:
3330*c8dee2aaSAndroid Build Coastguard Worker         case k_length_IntrinsicKind:
3331*c8dee2aaSAndroid Build Coastguard Worker         case k_log_IntrinsicKind:
3332*c8dee2aaSAndroid Build Coastguard Worker         case k_log2_IntrinsicKind:
3333*c8dee2aaSAndroid Build Coastguard Worker         case k_radians_IntrinsicKind:
3334*c8dee2aaSAndroid Build Coastguard Worker         case k_pow_IntrinsicKind:
3335*c8dee2aaSAndroid Build Coastguard Worker         case k_saturate_IntrinsicKind:
3336*c8dee2aaSAndroid Build Coastguard Worker         case k_sign_IntrinsicKind:
3337*c8dee2aaSAndroid Build Coastguard Worker         case k_sin_IntrinsicKind:
3338*c8dee2aaSAndroid Build Coastguard Worker         case k_sqrt_IntrinsicKind:
3339*c8dee2aaSAndroid Build Coastguard Worker         case k_storageBarrier_IntrinsicKind:
3340*c8dee2aaSAndroid Build Coastguard Worker         case k_tan_IntrinsicKind:
3341*c8dee2aaSAndroid Build Coastguard Worker         case k_workgroupBarrier_IntrinsicKind:
3342*c8dee2aaSAndroid Build Coastguard Worker         default:
3343*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic(call.function().name(), call);
3344*c8dee2aaSAndroid Build Coastguard Worker     }
3345*c8dee2aaSAndroid Build Coastguard Worker }
3346*c8dee2aaSAndroid Build Coastguard Worker 
3347*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kInverse2x2[] =
3348*c8dee2aaSAndroid Build Coastguard Worker      "fn mat2_inverse(m: mat2x2<f32>) -> mat2x2<f32> {"
3349*c8dee2aaSAndroid Build Coastguard Worker "\n"     "return mat2x2<f32>(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m));"
3350*c8dee2aaSAndroid Build Coastguard Worker "\n" "}"
3351*c8dee2aaSAndroid Build Coastguard Worker "\n";
3352*c8dee2aaSAndroid Build Coastguard Worker 
3353*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kInverse3x3[] =
3354*c8dee2aaSAndroid Build Coastguard Worker      "fn mat3_inverse(m: mat3x3<f32>) -> mat3x3<f32> {"
3355*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z;"
3356*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z;"
3357*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z;"
3358*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b01 =  a22*a11 - a12*a21;"
3359*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b11 = -a22*a10 + a12*a20;"
3360*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b21 =  a21*a10 - a11*a20;"
3361*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let det = a00*b01 + a01*b11 + a02*b21;"
3362*c8dee2aaSAndroid Build Coastguard Worker "\n"     "return mat3x3<f32>(b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),"
3363*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),"
3364*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det);"
3365*c8dee2aaSAndroid Build Coastguard Worker "\n" "}"
3366*c8dee2aaSAndroid Build Coastguard Worker "\n";
3367*c8dee2aaSAndroid Build Coastguard Worker 
3368*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kInverse4x4[] =
3369*c8dee2aaSAndroid Build Coastguard Worker      "fn mat4_inverse(m: mat4x4<f32>) -> mat4x4<f32>{"
3370*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z; let a03 = m[0].w;"
3371*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z; let a13 = m[1].w;"
3372*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z; let a23 = m[2].w;"
3373*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let a30 = m[3].x; let a31 = m[3].y; let a32 = m[3].z; let a33 = m[3].w;"
3374*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b00 = a00*a11 - a01*a10;"
3375*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b01 = a00*a12 - a02*a10;"
3376*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b02 = a00*a13 - a03*a10;"
3377*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b03 = a01*a12 - a02*a11;"
3378*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b04 = a01*a13 - a03*a11;"
3379*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b05 = a02*a13 - a03*a12;"
3380*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b06 = a20*a31 - a21*a30;"
3381*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b07 = a20*a32 - a22*a30;"
3382*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b08 = a20*a33 - a23*a30;"
3383*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b09 = a21*a32 - a22*a31;"
3384*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b10 = a21*a33 - a23*a31;"
3385*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let b11 = a22*a33 - a23*a32;"
3386*c8dee2aaSAndroid Build Coastguard Worker "\n"     "let det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;"
3387*c8dee2aaSAndroid Build Coastguard Worker "\n"     "return mat4x4<f32>(a11*b11 - a12*b10 + a13*b09,"
3388*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a02*b10 - a01*b11 - a03*b09,"
3389*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a31*b05 - a32*b04 + a33*b03,"
3390*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a22*b04 - a21*b05 - a23*b03,"
3391*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a12*b08 - a10*b11 - a13*b07,"
3392*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a00*b11 - a02*b08 + a03*b07,"
3393*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a32*b02 - a30*b05 - a33*b01,"
3394*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a20*b05 - a22*b02 + a23*b01,"
3395*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a10*b10 - a11*b08 + a13*b06,"
3396*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a01*b08 - a00*b10 - a03*b06,"
3397*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a30*b04 - a31*b02 + a33*b00,"
3398*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a21*b02 - a20*b04 - a23*b00,"
3399*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a11*b07 - a10*b09 - a12*b06,"
3400*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a00*b09 - a01*b07 + a02*b06,"
3401*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a31*b01 - a30*b03 - a32*b00,"
3402*c8dee2aaSAndroid Build Coastguard Worker "\n"                        "a20*b03 - a21*b01 + a22*b00) * (1/det);"
3403*c8dee2aaSAndroid Build Coastguard Worker "\n" "}"
3404*c8dee2aaSAndroid Build Coastguard Worker "\n";
3405*c8dee2aaSAndroid Build Coastguard Worker 
assembleInversePolyfill(const FunctionCall & call)3406*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleInversePolyfill(const FunctionCall& call) {
3407*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = call.arguments();
3408*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = arguments.front()->type();
3409*c8dee2aaSAndroid Build Coastguard Worker 
3410*c8dee2aaSAndroid Build Coastguard Worker     // The `inverse` intrinsic should only accept a single-argument square matrix.
3411*c8dee2aaSAndroid Build Coastguard Worker     // Once we implement f16 support, these polyfills will need to be updated to support `hmat`;
3412*c8dee2aaSAndroid Build Coastguard Worker     // for the time being, all floats in WGSL are f32, so we don't need to worry about precision.
3413*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(arguments.size() == 1);
3414*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isMatrix());
3415*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.rows() == type.columns());
3416*c8dee2aaSAndroid Build Coastguard Worker 
3417*c8dee2aaSAndroid Build Coastguard Worker     switch (type.slotCount()) {
3418*c8dee2aaSAndroid Build Coastguard Worker         case 4:
3419*c8dee2aaSAndroid Build Coastguard Worker             if (!fWrittenInverse2) {
3420*c8dee2aaSAndroid Build Coastguard Worker                 fWrittenInverse2 = true;
3421*c8dee2aaSAndroid Build Coastguard Worker                 fHeader.writeText(kInverse2x2);
3422*c8dee2aaSAndroid Build Coastguard Worker             }
3423*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("mat2_inverse", call);
3424*c8dee2aaSAndroid Build Coastguard Worker 
3425*c8dee2aaSAndroid Build Coastguard Worker         case 9:
3426*c8dee2aaSAndroid Build Coastguard Worker             if (!fWrittenInverse3) {
3427*c8dee2aaSAndroid Build Coastguard Worker                 fWrittenInverse3 = true;
3428*c8dee2aaSAndroid Build Coastguard Worker                 fHeader.writeText(kInverse3x3);
3429*c8dee2aaSAndroid Build Coastguard Worker             }
3430*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("mat3_inverse", call);
3431*c8dee2aaSAndroid Build Coastguard Worker 
3432*c8dee2aaSAndroid Build Coastguard Worker         case 16:
3433*c8dee2aaSAndroid Build Coastguard Worker             if (!fWrittenInverse4) {
3434*c8dee2aaSAndroid Build Coastguard Worker                 fWrittenInverse4 = true;
3435*c8dee2aaSAndroid Build Coastguard Worker                 fHeader.writeText(kInverse4x4);
3436*c8dee2aaSAndroid Build Coastguard Worker             }
3437*c8dee2aaSAndroid Build Coastguard Worker             return this->assembleSimpleIntrinsic("mat4_inverse", call);
3438*c8dee2aaSAndroid Build Coastguard Worker 
3439*c8dee2aaSAndroid Build Coastguard Worker         default:
3440*c8dee2aaSAndroid Build Coastguard Worker             // We only support square matrices.
3441*c8dee2aaSAndroid Build Coastguard Worker             SkUNREACHABLE;
3442*c8dee2aaSAndroid Build Coastguard Worker     }
3443*c8dee2aaSAndroid Build Coastguard Worker }
3444*c8dee2aaSAndroid Build Coastguard Worker 
assembleFunctionCall(const FunctionCall & call,Precedence parentPrecedence)3445*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleFunctionCall(const FunctionCall& call,
3446*c8dee2aaSAndroid Build Coastguard Worker                                                     Precedence parentPrecedence) {
3447*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration& func = call.function();
3448*c8dee2aaSAndroid Build Coastguard Worker     std::string result;
3449*c8dee2aaSAndroid Build Coastguard Worker 
3450*c8dee2aaSAndroid Build Coastguard Worker     // Many intrinsics need to be rewritten in WGSL.
3451*c8dee2aaSAndroid Build Coastguard Worker     if (func.isIntrinsic()) {
3452*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleIntrinsicCall(call, func.intrinsicKind(), parentPrecedence);
3453*c8dee2aaSAndroid Build Coastguard Worker     }
3454*c8dee2aaSAndroid Build Coastguard Worker 
3455*c8dee2aaSAndroid Build Coastguard Worker     // We implement function out-parameters by declaring them as pointers. SkSL follows GLSL's
3456*c8dee2aaSAndroid Build Coastguard Worker     // out-parameter semantics, in which out-parameters are only written back to the original
3457*c8dee2aaSAndroid Build Coastguard Worker     // variable after the function's execution is complete (see
3458*c8dee2aaSAndroid Build Coastguard Worker     // https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Parameters).
3459*c8dee2aaSAndroid Build Coastguard Worker     //
3460*c8dee2aaSAndroid Build Coastguard Worker     // In addition, SkSL supports swizzles and array index expressions to be passed into
3461*c8dee2aaSAndroid Build Coastguard Worker     // out-parameters; however, WGSL does not allow taking their address into a pointer.
3462*c8dee2aaSAndroid Build Coastguard Worker     //
3463*c8dee2aaSAndroid Build Coastguard Worker     // We support these by using LValues to create temporary copies and then pass pointers to the
3464*c8dee2aaSAndroid Build Coastguard Worker     // copies. Once the function returns, we copy the values back to the LValue.
3465*c8dee2aaSAndroid Build Coastguard Worker 
3466*c8dee2aaSAndroid Build Coastguard Worker     // First detect which arguments are passed to out-parameters.
3467*c8dee2aaSAndroid Build Coastguard Worker     // TODO: rewrite this method in terms of LValues.
3468*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& args = call.arguments();
3469*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<Variable* const> params = func.parameters();
3470*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(SkToSizeT(args.size()) == params.size());
3471*c8dee2aaSAndroid Build Coastguard Worker 
3472*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, std::unique_ptr<LValue>> writeback;
3473*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, std::string> substituteArgument;
3474*c8dee2aaSAndroid Build Coastguard Worker     writeback.reserve_exact(args.size());
3475*c8dee2aaSAndroid Build Coastguard Worker     substituteArgument.reserve_exact(args.size());
3476*c8dee2aaSAndroid Build Coastguard Worker 
3477*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < args.size(); ++index) {
3478*c8dee2aaSAndroid Build Coastguard Worker         if (params[index]->modifierFlags() & ModifierFlag::kOut) {
3479*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<LValue> lvalue = this->makeLValue(*args[index]);
3480*c8dee2aaSAndroid Build Coastguard Worker             if (params[index]->modifierFlags() & ModifierFlag::kIn) {
3481*c8dee2aaSAndroid Build Coastguard Worker                 // Load the lvalue's contents into the substitute argument.
3482*c8dee2aaSAndroid Build Coastguard Worker                 substituteArgument.push_back(this->writeScratchVar(args[index]->type(),
3483*c8dee2aaSAndroid Build Coastguard Worker                                                                    lvalue->load()));
3484*c8dee2aaSAndroid Build Coastguard Worker             } else {
3485*c8dee2aaSAndroid Build Coastguard Worker                 // Create a substitute argument, but leave it uninitialized.
3486*c8dee2aaSAndroid Build Coastguard Worker                 substituteArgument.push_back(this->writeScratchVar(args[index]->type()));
3487*c8dee2aaSAndroid Build Coastguard Worker             }
3488*c8dee2aaSAndroid Build Coastguard Worker             writeback.push_back(std::move(lvalue));
3489*c8dee2aaSAndroid Build Coastguard Worker         } else {
3490*c8dee2aaSAndroid Build Coastguard Worker             substituteArgument.push_back(std::string());
3491*c8dee2aaSAndroid Build Coastguard Worker             writeback.push_back(nullptr);
3492*c8dee2aaSAndroid Build Coastguard Worker         }
3493*c8dee2aaSAndroid Build Coastguard Worker     }
3494*c8dee2aaSAndroid Build Coastguard Worker 
3495*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = this->assembleName(func.mangledName());
3496*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back('(');
3497*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
3498*c8dee2aaSAndroid Build Coastguard Worker 
3499*c8dee2aaSAndroid Build Coastguard Worker     if (std::string funcDepArgs = this->functionDependencyArgs(func); !funcDepArgs.empty()) {
3500*c8dee2aaSAndroid Build Coastguard Worker         expr += funcDepArgs;
3501*c8dee2aaSAndroid Build Coastguard Worker         separator();
3502*c8dee2aaSAndroid Build Coastguard Worker     }
3503*c8dee2aaSAndroid Build Coastguard Worker 
3504*c8dee2aaSAndroid Build Coastguard Worker     // Pass the function arguments, or any substitutes as needed.
3505*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < args.size(); ++index) {
3506*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
3507*c8dee2aaSAndroid Build Coastguard Worker         if (!substituteArgument[index].empty()) {
3508*c8dee2aaSAndroid Build Coastguard Worker             // We need to take the address of the variable and pass it down as a pointer.
3509*c8dee2aaSAndroid Build Coastguard Worker             expr += '&' + substituteArgument[index];
3510*c8dee2aaSAndroid Build Coastguard Worker         } else if (args[index]->type().isSampler()) {
3511*c8dee2aaSAndroid Build Coastguard Worker             // If the argument is a sampler, we need to pass the texture _and_ its associated
3512*c8dee2aaSAndroid Build Coastguard Worker             // sampler. (Function parameter lists also convert sampler parameters into a matching
3513*c8dee2aaSAndroid Build Coastguard Worker             // texture/sampler parameter pair.)
3514*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleExpression(*args[index], Precedence::kSequence);
3515*c8dee2aaSAndroid Build Coastguard Worker             expr += kTextureSuffix;
3516*c8dee2aaSAndroid Build Coastguard Worker             expr += ", ";
3517*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleExpression(*args[index], Precedence::kSequence);
3518*c8dee2aaSAndroid Build Coastguard Worker             expr += kSamplerSuffix;
3519*c8dee2aaSAndroid Build Coastguard Worker         } else if (args[index]->type().isUnsizedArray()) {
3520*c8dee2aaSAndroid Build Coastguard Worker             // If the array is in the parameter storage space then manually just pass it through
3521*c8dee2aaSAndroid Build Coastguard Worker             // since it is already a pointer.
3522*c8dee2aaSAndroid Build Coastguard Worker             if (args[index]->is<VariableReference>()) {
3523*c8dee2aaSAndroid Build Coastguard Worker                 const Variable* v = args[index]->as<VariableReference>().variable();
3524*c8dee2aaSAndroid Build Coastguard Worker                 // A variable reference to an unsized array should always be a parameter,
3525*c8dee2aaSAndroid Build Coastguard Worker                 // because unsized arrays coming from uniforms will have the `FieldAccess`
3526*c8dee2aaSAndroid Build Coastguard Worker                 // expression type.
3527*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(v->storage() == Variable::Storage::kParameter);
3528*c8dee2aaSAndroid Build Coastguard Worker                 expr += this->assembleName(v->mangledName());
3529*c8dee2aaSAndroid Build Coastguard Worker             } else {
3530*c8dee2aaSAndroid Build Coastguard Worker                 expr += "&(" + this->assembleExpression(*args[index], Precedence::kSequence) + ")";
3531*c8dee2aaSAndroid Build Coastguard Worker             }
3532*c8dee2aaSAndroid Build Coastguard Worker         } else {
3533*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleExpression(*args[index], Precedence::kSequence);
3534*c8dee2aaSAndroid Build Coastguard Worker         }
3535*c8dee2aaSAndroid Build Coastguard Worker     }
3536*c8dee2aaSAndroid Build Coastguard Worker     expr += ')';
3537*c8dee2aaSAndroid Build Coastguard Worker 
3538*c8dee2aaSAndroid Build Coastguard Worker     if (call.type().isVoid()) {
3539*c8dee2aaSAndroid Build Coastguard Worker         // Making function calls that result in `void` is only valid in on the left side of a
3540*c8dee2aaSAndroid Build Coastguard Worker         // comma-sequence, or in a top-level statement. Emit the function call as a top-level
3541*c8dee2aaSAndroid Build Coastguard Worker         // statement and return an empty string, as the result will not be used.
3542*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(parentPrecedence >= Precedence::kSequence);
3543*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
3544*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
3545*c8dee2aaSAndroid Build Coastguard Worker     } else {
3546*c8dee2aaSAndroid Build Coastguard Worker         result = this->writeScratchLet(expr);
3547*c8dee2aaSAndroid Build Coastguard Worker     }
3548*c8dee2aaSAndroid Build Coastguard Worker 
3549*c8dee2aaSAndroid Build Coastguard Worker     // Write the substitute arguments back into their lvalues.
3550*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < args.size(); ++index) {
3551*c8dee2aaSAndroid Build Coastguard Worker         if (!substituteArgument[index].empty()) {
3552*c8dee2aaSAndroid Build Coastguard Worker             this->writeLine(writeback[index]->store(substituteArgument[index]));
3553*c8dee2aaSAndroid Build Coastguard Worker         }
3554*c8dee2aaSAndroid Build Coastguard Worker     }
3555*c8dee2aaSAndroid Build Coastguard Worker 
3556*c8dee2aaSAndroid Build Coastguard Worker     // Return the result of invoking the function.
3557*c8dee2aaSAndroid Build Coastguard Worker     return result;
3558*c8dee2aaSAndroid Build Coastguard Worker }
3559*c8dee2aaSAndroid Build Coastguard Worker 
assembleIndexExpression(const IndexExpression & i)3560*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleIndexExpression(const IndexExpression& i) {
3561*c8dee2aaSAndroid Build Coastguard Worker     // Put the index value into a let-expression.
3562*c8dee2aaSAndroid Build Coastguard Worker     std::string idx = this->writeNontrivialScratchLet(*i.index(), Precedence::kExpression);
3563*c8dee2aaSAndroid Build Coastguard Worker     return this->assembleExpression(*i.base(), Precedence::kPostfix) + "[" + idx + "]";
3564*c8dee2aaSAndroid Build Coastguard Worker }
3565*c8dee2aaSAndroid Build Coastguard Worker 
assembleLiteral(const Literal & l)3566*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleLiteral(const Literal& l) {
3567*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = l.type();
3568*c8dee2aaSAndroid Build Coastguard Worker     if (type.isFloat() || type.isBoolean()) {
3569*c8dee2aaSAndroid Build Coastguard Worker         return l.description(OperatorPrecedence::kExpression);
3570*c8dee2aaSAndroid Build Coastguard Worker     }
3571*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isInteger());
3572*c8dee2aaSAndroid Build Coastguard Worker     if (type.matches(*fContext.fTypes.fUInt)) {
3573*c8dee2aaSAndroid Build Coastguard Worker         return std::to_string(l.intValue() & 0xffffffff) + "u";
3574*c8dee2aaSAndroid Build Coastguard Worker     } else if (type.matches(*fContext.fTypes.fUShort)) {
3575*c8dee2aaSAndroid Build Coastguard Worker         return std::to_string(l.intValue() & 0xffff) + "u";
3576*c8dee2aaSAndroid Build Coastguard Worker     } else {
3577*c8dee2aaSAndroid Build Coastguard Worker         return std::to_string(l.intValue());
3578*c8dee2aaSAndroid Build Coastguard Worker     }
3579*c8dee2aaSAndroid Build Coastguard Worker }
3580*c8dee2aaSAndroid Build Coastguard Worker 
assembleIncrementExpr(const Type & type)3581*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleIncrementExpr(const Type& type) {
3582*c8dee2aaSAndroid Build Coastguard Worker     // `type(`
3583*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, type);
3584*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back('(');
3585*c8dee2aaSAndroid Build Coastguard Worker 
3586*c8dee2aaSAndroid Build Coastguard Worker     // `1, 1, 1...)`
3587*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
3588*c8dee2aaSAndroid Build Coastguard Worker     for (int slots = type.slotCount(); slots > 0; --slots) {
3589*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
3590*c8dee2aaSAndroid Build Coastguard Worker         expr += "1";
3591*c8dee2aaSAndroid Build Coastguard Worker     }
3592*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back(')');
3593*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3594*c8dee2aaSAndroid Build Coastguard Worker }
3595*c8dee2aaSAndroid Build Coastguard Worker 
assemblePrefixExpression(const PrefixExpression & p,Precedence parentPrecedence)3596*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assemblePrefixExpression(const PrefixExpression& p,
3597*c8dee2aaSAndroid Build Coastguard Worker                                                         Precedence parentPrecedence) {
3598*c8dee2aaSAndroid Build Coastguard Worker     // Unary + does nothing, so we omit it from the output.
3599*c8dee2aaSAndroid Build Coastguard Worker     Operator op = p.getOperator();
3600*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::PLUS) {
3601*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleExpression(*p.operand(), Precedence::kPrefix);
3602*c8dee2aaSAndroid Build Coastguard Worker     }
3603*c8dee2aaSAndroid Build Coastguard Worker 
3604*c8dee2aaSAndroid Build Coastguard Worker     // Pre-increment/decrement expressions have no direct equivalent in WGSL.
3605*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::PLUSPLUS || op.kind() == Operator::Kind::MINUSMINUS) {
3606*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<LValue> lvalue = this->makeLValue(*p.operand());
3607*c8dee2aaSAndroid Build Coastguard Worker         if (!lvalue) {
3608*c8dee2aaSAndroid Build Coastguard Worker             return "";
3609*c8dee2aaSAndroid Build Coastguard Worker         }
3610*c8dee2aaSAndroid Build Coastguard Worker 
3611*c8dee2aaSAndroid Build Coastguard Worker         // Generate the new value: `lvalue + type(1, 1, 1...)`.
3612*c8dee2aaSAndroid Build Coastguard Worker         std::string newValue =
3613*c8dee2aaSAndroid Build Coastguard Worker                 lvalue->load() +
3614*c8dee2aaSAndroid Build Coastguard Worker                 (p.getOperator().kind() == Operator::Kind::PLUSPLUS ? " + " : " - ") +
3615*c8dee2aaSAndroid Build Coastguard Worker                 this->assembleIncrementExpr(p.operand()->type());
3616*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(lvalue->store(newValue));
3617*c8dee2aaSAndroid Build Coastguard Worker         return lvalue->load();
3618*c8dee2aaSAndroid Build Coastguard Worker     }
3619*c8dee2aaSAndroid Build Coastguard Worker 
3620*c8dee2aaSAndroid Build Coastguard Worker     // WGSL natively supports unary negation/not expressions (!,~,-).
3621*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op.kind() == OperatorKind::LOGICALNOT ||
3622*c8dee2aaSAndroid Build Coastguard Worker              op.kind() == OperatorKind::BITWISENOT ||
3623*c8dee2aaSAndroid Build Coastguard Worker              op.kind() == OperatorKind::MINUS);
3624*c8dee2aaSAndroid Build Coastguard Worker 
3625*c8dee2aaSAndroid Build Coastguard Worker     // The unary negation operator only applies to scalars and vectors. For other mathematical
3626*c8dee2aaSAndroid Build Coastguard Worker     // objects (such as matrices) we can express it as a multiplication by -1.
3627*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
3628*c8dee2aaSAndroid Build Coastguard Worker     const bool needsNegation = op.kind() == Operator::Kind::MINUS &&
3629*c8dee2aaSAndroid Build Coastguard Worker                                !p.operand()->type().isScalar() && !p.operand()->type().isVector();
3630*c8dee2aaSAndroid Build Coastguard Worker     const bool needParens = needsNegation || Precedence::kPrefix >= parentPrecedence;
3631*c8dee2aaSAndroid Build Coastguard Worker 
3632*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
3633*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back('(');
3634*c8dee2aaSAndroid Build Coastguard Worker     }
3635*c8dee2aaSAndroid Build Coastguard Worker 
3636*c8dee2aaSAndroid Build Coastguard Worker     if (needsNegation) {
3637*c8dee2aaSAndroid Build Coastguard Worker         expr += "-1.0 * ";
3638*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*p.operand(), Precedence::kMultiplicative);
3639*c8dee2aaSAndroid Build Coastguard Worker     } else {
3640*c8dee2aaSAndroid Build Coastguard Worker         expr += p.getOperator().tightOperatorName();
3641*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*p.operand(), Precedence::kPrefix);
3642*c8dee2aaSAndroid Build Coastguard Worker     }
3643*c8dee2aaSAndroid Build Coastguard Worker 
3644*c8dee2aaSAndroid Build Coastguard Worker     if (needParens) {
3645*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back(')');
3646*c8dee2aaSAndroid Build Coastguard Worker     }
3647*c8dee2aaSAndroid Build Coastguard Worker 
3648*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3649*c8dee2aaSAndroid Build Coastguard Worker }
3650*c8dee2aaSAndroid Build Coastguard Worker 
assemblePostfixExpression(const PostfixExpression & p,Precedence parentPrecedence)3651*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assemblePostfixExpression(const PostfixExpression& p,
3652*c8dee2aaSAndroid Build Coastguard Worker                                                          Precedence parentPrecedence) {
3653*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(p.getOperator().kind() == Operator::Kind::PLUSPLUS ||
3654*c8dee2aaSAndroid Build Coastguard Worker              p.getOperator().kind() == Operator::Kind::MINUSMINUS);
3655*c8dee2aaSAndroid Build Coastguard Worker 
3656*c8dee2aaSAndroid Build Coastguard Worker     // Post-increment/decrement expressions have no direct equivalent in WGSL; they do exist as a
3657*c8dee2aaSAndroid Build Coastguard Worker     // standalone statement for convenience, but these aren't the same as SkSL's post-increments.
3658*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue = this->makeLValue(*p.operand());
3659*c8dee2aaSAndroid Build Coastguard Worker     if (!lvalue) {
3660*c8dee2aaSAndroid Build Coastguard Worker         return "";
3661*c8dee2aaSAndroid Build Coastguard Worker     }
3662*c8dee2aaSAndroid Build Coastguard Worker 
3663*c8dee2aaSAndroid Build Coastguard Worker     // If the expression is used, create a let-copy of the original value.
3664*c8dee2aaSAndroid Build Coastguard Worker     // (At statement-level precedence, we know the value is unused and can skip this let-copy.)
3665*c8dee2aaSAndroid Build Coastguard Worker     std::string originalValue;
3666*c8dee2aaSAndroid Build Coastguard Worker     if (parentPrecedence != Precedence::kStatement) {
3667*c8dee2aaSAndroid Build Coastguard Worker         originalValue = this->writeScratchLet(lvalue->load());
3668*c8dee2aaSAndroid Build Coastguard Worker     }
3669*c8dee2aaSAndroid Build Coastguard Worker     // Generate the new value: `lvalue + type(1, 1, 1...)`.
3670*c8dee2aaSAndroid Build Coastguard Worker     std::string newValue = lvalue->load() +
3671*c8dee2aaSAndroid Build Coastguard Worker                            (p.getOperator().kind() == Operator::Kind::PLUSPLUS ? " + " : " - ") +
3672*c8dee2aaSAndroid Build Coastguard Worker                            this->assembleIncrementExpr(p.operand()->type());
3673*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(lvalue->store(newValue));
3674*c8dee2aaSAndroid Build Coastguard Worker 
3675*c8dee2aaSAndroid Build Coastguard Worker     return originalValue;
3676*c8dee2aaSAndroid Build Coastguard Worker }
3677*c8dee2aaSAndroid Build Coastguard Worker 
assembleSwizzle(const Swizzle & swizzle)3678*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleSwizzle(const Swizzle& swizzle) {
3679*c8dee2aaSAndroid Build Coastguard Worker     return this->assembleExpression(*swizzle.base(), Precedence::kPostfix) + "." +
3680*c8dee2aaSAndroid Build Coastguard Worker            Swizzle::MaskString(swizzle.components());
3681*c8dee2aaSAndroid Build Coastguard Worker }
3682*c8dee2aaSAndroid Build Coastguard Worker 
writeScratchVar(const Type & type,const std::string & value)3683*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::writeScratchVar(const Type& type, const std::string& value) {
3684*c8dee2aaSAndroid Build Coastguard Worker     std::string scratchVarName = "_skTemp" + std::to_string(fScratchCount++);
3685*c8dee2aaSAndroid Build Coastguard Worker     this->write("var ");
3686*c8dee2aaSAndroid Build Coastguard Worker     this->write(scratchVarName);
3687*c8dee2aaSAndroid Build Coastguard Worker     this->write(": ");
3688*c8dee2aaSAndroid Build Coastguard Worker     this->write(to_wgsl_type(fContext, type));
3689*c8dee2aaSAndroid Build Coastguard Worker     if (!value.empty()) {
3690*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
3691*c8dee2aaSAndroid Build Coastguard Worker         this->write(value);
3692*c8dee2aaSAndroid Build Coastguard Worker     }
3693*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(";");
3694*c8dee2aaSAndroid Build Coastguard Worker     return scratchVarName;
3695*c8dee2aaSAndroid Build Coastguard Worker }
3696*c8dee2aaSAndroid Build Coastguard Worker 
writeScratchLet(const std::string & expr,bool isCompileTimeConstant)3697*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::writeScratchLet(const std::string& expr,
3698*c8dee2aaSAndroid Build Coastguard Worker                                                bool isCompileTimeConstant) {
3699*c8dee2aaSAndroid Build Coastguard Worker     std::string scratchVarName = "_skTemp" + std::to_string(fScratchCount++);
3700*c8dee2aaSAndroid Build Coastguard Worker     this->write(fAtFunctionScope && !isCompileTimeConstant ? "let " : "const ");
3701*c8dee2aaSAndroid Build Coastguard Worker     this->write(scratchVarName);
3702*c8dee2aaSAndroid Build Coastguard Worker     this->write(" = ");
3703*c8dee2aaSAndroid Build Coastguard Worker     this->write(expr);
3704*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(";");
3705*c8dee2aaSAndroid Build Coastguard Worker     return scratchVarName;
3706*c8dee2aaSAndroid Build Coastguard Worker }
3707*c8dee2aaSAndroid Build Coastguard Worker 
writeScratchLet(const Expression & expr,Precedence parentPrecedence)3708*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::writeScratchLet(const Expression& expr,
3709*c8dee2aaSAndroid Build Coastguard Worker                                                Precedence parentPrecedence) {
3710*c8dee2aaSAndroid Build Coastguard Worker     return this->writeScratchLet(this->assembleExpression(expr, parentPrecedence));
3711*c8dee2aaSAndroid Build Coastguard Worker }
3712*c8dee2aaSAndroid Build Coastguard Worker 
writeNontrivialScratchLet(const Expression & expr,Precedence parentPrecedence)3713*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::writeNontrivialScratchLet(const Expression& expr,
3714*c8dee2aaSAndroid Build Coastguard Worker                                                          Precedence parentPrecedence) {
3715*c8dee2aaSAndroid Build Coastguard Worker     std::string result = this->assembleExpression(expr, parentPrecedence);
3716*c8dee2aaSAndroid Build Coastguard Worker     return is_nontrivial_expression(expr)
3717*c8dee2aaSAndroid Build Coastguard Worker                    ? this->writeScratchLet(result, Analysis::IsCompileTimeConstant(expr))
3718*c8dee2aaSAndroid Build Coastguard Worker                    : result;
3719*c8dee2aaSAndroid Build Coastguard Worker }
3720*c8dee2aaSAndroid Build Coastguard Worker 
assembleTernaryExpression(const TernaryExpression & t,Precedence parentPrecedence)3721*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleTernaryExpression(const TernaryExpression& t,
3722*c8dee2aaSAndroid Build Coastguard Worker                                                          Precedence parentPrecedence) {
3723*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
3724*c8dee2aaSAndroid Build Coastguard Worker 
3725*c8dee2aaSAndroid Build Coastguard Worker     // The trivial case is when neither branch has side effects and evaluate to a scalar or vector
3726*c8dee2aaSAndroid Build Coastguard Worker     // type. This can be represented with a call to the WGSL `select` intrinsic. Select doesn't
3727*c8dee2aaSAndroid Build Coastguard Worker     // support short-circuiting, so we should only use it when both the true- and false-expressions
3728*c8dee2aaSAndroid Build Coastguard Worker     // are trivial to evaluate.
3729*c8dee2aaSAndroid Build Coastguard Worker     if ((t.type().isScalar() || t.type().isVector()) &&
3730*c8dee2aaSAndroid Build Coastguard Worker         !Analysis::HasSideEffects(*t.test()) &&
3731*c8dee2aaSAndroid Build Coastguard Worker         Analysis::IsTrivialExpression(*t.ifTrue()) &&
3732*c8dee2aaSAndroid Build Coastguard Worker         Analysis::IsTrivialExpression(*t.ifFalse())) {
3733*c8dee2aaSAndroid Build Coastguard Worker 
3734*c8dee2aaSAndroid Build Coastguard Worker         bool needParens = Precedence::kTernary >= parentPrecedence;
3735*c8dee2aaSAndroid Build Coastguard Worker         if (needParens) {
3736*c8dee2aaSAndroid Build Coastguard Worker             expr.push_back('(');
3737*c8dee2aaSAndroid Build Coastguard Worker         }
3738*c8dee2aaSAndroid Build Coastguard Worker         expr += "select(";
3739*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*t.ifFalse(), Precedence::kSequence);
3740*c8dee2aaSAndroid Build Coastguard Worker         expr += ", ";
3741*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*t.ifTrue(), Precedence::kSequence);
3742*c8dee2aaSAndroid Build Coastguard Worker         expr += ", ";
3743*c8dee2aaSAndroid Build Coastguard Worker 
3744*c8dee2aaSAndroid Build Coastguard Worker         bool isVector = t.type().isVector();
3745*c8dee2aaSAndroid Build Coastguard Worker         if (isVector) {
3746*c8dee2aaSAndroid Build Coastguard Worker             // Splat the condition expression into a vector.
3747*c8dee2aaSAndroid Build Coastguard Worker             expr += String::printf("vec%d<bool>(", t.type().columns());
3748*c8dee2aaSAndroid Build Coastguard Worker         }
3749*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*t.test(), Precedence::kSequence);
3750*c8dee2aaSAndroid Build Coastguard Worker         if (isVector) {
3751*c8dee2aaSAndroid Build Coastguard Worker             expr.push_back(')');
3752*c8dee2aaSAndroid Build Coastguard Worker         }
3753*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back(')');
3754*c8dee2aaSAndroid Build Coastguard Worker         if (needParens) {
3755*c8dee2aaSAndroid Build Coastguard Worker             expr.push_back(')');
3756*c8dee2aaSAndroid Build Coastguard Worker         }
3757*c8dee2aaSAndroid Build Coastguard Worker     } else {
3758*c8dee2aaSAndroid Build Coastguard Worker         // WGSL does not support ternary expressions. Instead, we hoist the expression out into the
3759*c8dee2aaSAndroid Build Coastguard Worker         // surrounding block, convert it into an if statement, and write the result to a synthesized
3760*c8dee2aaSAndroid Build Coastguard Worker         // variable. Instead of the original expression, we return that variable.
3761*c8dee2aaSAndroid Build Coastguard Worker         expr = this->writeScratchVar(t.ifTrue()->type());
3762*c8dee2aaSAndroid Build Coastguard Worker 
3763*c8dee2aaSAndroid Build Coastguard Worker         std::string testExpr = this->assembleExpression(*t.test(), Precedence::kExpression);
3764*c8dee2aaSAndroid Build Coastguard Worker         this->write("if ");
3765*c8dee2aaSAndroid Build Coastguard Worker         this->write(testExpr);
3766*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" {");
3767*c8dee2aaSAndroid Build Coastguard Worker 
3768*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
3769*c8dee2aaSAndroid Build Coastguard Worker         std::string trueExpr = this->assembleExpression(*t.ifTrue(), Precedence::kAssignment);
3770*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
3771*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
3772*c8dee2aaSAndroid Build Coastguard Worker         this->write(trueExpr);
3773*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
3774*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
3775*c8dee2aaSAndroid Build Coastguard Worker 
3776*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("} else {");
3777*c8dee2aaSAndroid Build Coastguard Worker 
3778*c8dee2aaSAndroid Build Coastguard Worker         ++fIndentation;
3779*c8dee2aaSAndroid Build Coastguard Worker         std::string falseExpr = this->assembleExpression(*t.ifFalse(), Precedence::kAssignment);
3780*c8dee2aaSAndroid Build Coastguard Worker         this->write(expr);
3781*c8dee2aaSAndroid Build Coastguard Worker         this->write(" = ");
3782*c8dee2aaSAndroid Build Coastguard Worker         this->write(falseExpr);
3783*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
3784*c8dee2aaSAndroid Build Coastguard Worker         --fIndentation;
3785*c8dee2aaSAndroid Build Coastguard Worker 
3786*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("}");
3787*c8dee2aaSAndroid Build Coastguard Worker     }
3788*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3789*c8dee2aaSAndroid Build Coastguard Worker }
3790*c8dee2aaSAndroid Build Coastguard Worker 
variablePrefix(const Variable & v)3791*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::variablePrefix(const Variable& v) {
3792*c8dee2aaSAndroid Build Coastguard Worker     if (v.storage() == Variable::Storage::kGlobal) {
3793*c8dee2aaSAndroid Build Coastguard Worker         // If the field refers to a pipeline IO parameter, then we access it via the synthesized IO
3794*c8dee2aaSAndroid Build Coastguard Worker         // structs. We make an explicit exception for `sk_PointSize` which we declare as a
3795*c8dee2aaSAndroid Build Coastguard Worker         // placeholder variable in global scope as it is not supported by WebGPU as a pipeline IO
3796*c8dee2aaSAndroid Build Coastguard Worker         // parameter (see comments in `writeStageOutputStruct`).
3797*c8dee2aaSAndroid Build Coastguard Worker         if (v.modifierFlags() & ModifierFlag::kIn) {
3798*c8dee2aaSAndroid Build Coastguard Worker             return "_stageIn.";
3799*c8dee2aaSAndroid Build Coastguard Worker         }
3800*c8dee2aaSAndroid Build Coastguard Worker         if (v.modifierFlags() & ModifierFlag::kOut) {
3801*c8dee2aaSAndroid Build Coastguard Worker             return "(*_stageOut).";
3802*c8dee2aaSAndroid Build Coastguard Worker         }
3803*c8dee2aaSAndroid Build Coastguard Worker 
3804*c8dee2aaSAndroid Build Coastguard Worker         // If the field refers to an anonymous-interface-block structure, access it via the
3805*c8dee2aaSAndroid Build Coastguard Worker         // synthesized `_uniform0` or `_storage1` global.
3806*c8dee2aaSAndroid Build Coastguard Worker         if (const InterfaceBlock* ib = v.interfaceBlock()) {
3807*c8dee2aaSAndroid Build Coastguard Worker             const Type& ibType = ib->var()->type().componentType();
3808*c8dee2aaSAndroid Build Coastguard Worker             if (const std::string* ibName = fInterfaceBlockNameMap.find(&ibType)) {
3809*c8dee2aaSAndroid Build Coastguard Worker                 return *ibName + '.';
3810*c8dee2aaSAndroid Build Coastguard Worker             }
3811*c8dee2aaSAndroid Build Coastguard Worker         }
3812*c8dee2aaSAndroid Build Coastguard Worker 
3813*c8dee2aaSAndroid Build Coastguard Worker         // If the field refers to an top-level uniform, access it via the synthesized
3814*c8dee2aaSAndroid Build Coastguard Worker         // `_globalUniforms` global. (Note that this should only occur in test code; Skia will
3815*c8dee2aaSAndroid Build Coastguard Worker         // always put uniforms in an interface block.)
3816*c8dee2aaSAndroid Build Coastguard Worker         if (is_in_global_uniforms(v)) {
3817*c8dee2aaSAndroid Build Coastguard Worker             return "_globalUniforms.";
3818*c8dee2aaSAndroid Build Coastguard Worker         }
3819*c8dee2aaSAndroid Build Coastguard Worker     }
3820*c8dee2aaSAndroid Build Coastguard Worker 
3821*c8dee2aaSAndroid Build Coastguard Worker     return "";
3822*c8dee2aaSAndroid Build Coastguard Worker }
3823*c8dee2aaSAndroid Build Coastguard Worker 
variableReferenceNameForLValue(const VariableReference & r)3824*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::variableReferenceNameForLValue(const VariableReference& r) {
3825*c8dee2aaSAndroid Build Coastguard Worker     const Variable& v = *r.variable();
3826*c8dee2aaSAndroid Build Coastguard Worker 
3827*c8dee2aaSAndroid Build Coastguard Worker     if (v.storage() == Variable::Storage::kParameter &&
3828*c8dee2aaSAndroid Build Coastguard Worker          (v.modifierFlags() & ModifierFlag::kOut || v.type().isUnsizedArray())) {
3829*c8dee2aaSAndroid Build Coastguard Worker         // This is an out-parameter or unsized array parameter; it's pointer-typed, so we need to
3830*c8dee2aaSAndroid Build Coastguard Worker         // dereference it. We wrap the dereference in parentheses, in case the value is used in an
3831*c8dee2aaSAndroid Build Coastguard Worker         // access expression later.
3832*c8dee2aaSAndroid Build Coastguard Worker         return "(*" + this->assembleName(v.mangledName()) + ')';
3833*c8dee2aaSAndroid Build Coastguard Worker     }
3834*c8dee2aaSAndroid Build Coastguard Worker 
3835*c8dee2aaSAndroid Build Coastguard Worker     return this->variablePrefix(v) + this->assembleName(v.mangledName());
3836*c8dee2aaSAndroid Build Coastguard Worker }
3837*c8dee2aaSAndroid Build Coastguard Worker 
assembleVariableReference(const VariableReference & r)3838*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleVariableReference(const VariableReference& r) {
3839*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/294274678): Correctly handle RTFlip for built-ins.
3840*c8dee2aaSAndroid Build Coastguard Worker     const Variable& v = *r.variable();
3841*c8dee2aaSAndroid Build Coastguard Worker 
3842*c8dee2aaSAndroid Build Coastguard Worker     // Insert a conversion expression if this is a built-in variable whose type differs from the
3843*c8dee2aaSAndroid Build Coastguard Worker     // SkSL.
3844*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
3845*c8dee2aaSAndroid Build Coastguard Worker     std::optional<std::string_view> conversion = needs_builtin_type_conversion(v);
3846*c8dee2aaSAndroid Build Coastguard Worker     if (conversion.has_value()) {
3847*c8dee2aaSAndroid Build Coastguard Worker         expr += *conversion;
3848*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back('(');
3849*c8dee2aaSAndroid Build Coastguard Worker     }
3850*c8dee2aaSAndroid Build Coastguard Worker 
3851*c8dee2aaSAndroid Build Coastguard Worker     expr += this->variableReferenceNameForLValue(r);
3852*c8dee2aaSAndroid Build Coastguard Worker 
3853*c8dee2aaSAndroid Build Coastguard Worker     if (conversion.has_value()) {
3854*c8dee2aaSAndroid Build Coastguard Worker         expr.push_back(')');
3855*c8dee2aaSAndroid Build Coastguard Worker     }
3856*c8dee2aaSAndroid Build Coastguard Worker 
3857*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3858*c8dee2aaSAndroid Build Coastguard Worker }
3859*c8dee2aaSAndroid Build Coastguard Worker 
assembleAnyConstructor(const AnyConstructor & c)3860*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleAnyConstructor(const AnyConstructor& c) {
3861*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, c.type());
3862*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back('(');
3863*c8dee2aaSAndroid Build Coastguard Worker     auto separator = SkSL::String::Separator();
3864*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& e : c.argumentSpan()) {
3865*c8dee2aaSAndroid Build Coastguard Worker         expr += separator();
3866*c8dee2aaSAndroid Build Coastguard Worker         expr += this->assembleExpression(*e, Precedence::kSequence);
3867*c8dee2aaSAndroid Build Coastguard Worker     }
3868*c8dee2aaSAndroid Build Coastguard Worker     expr.push_back(')');
3869*c8dee2aaSAndroid Build Coastguard Worker     return expr;
3870*c8dee2aaSAndroid Build Coastguard Worker }
3871*c8dee2aaSAndroid Build Coastguard Worker 
assembleConstructorCompound(const ConstructorCompound & c)3872*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleConstructorCompound(const ConstructorCompound& c) {
3873*c8dee2aaSAndroid Build Coastguard Worker     if (c.type().isVector()) {
3874*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleConstructorCompoundVector(c);
3875*c8dee2aaSAndroid Build Coastguard Worker     } else if (c.type().isMatrix()) {
3876*c8dee2aaSAndroid Build Coastguard Worker         return this->assembleConstructorCompoundMatrix(c);
3877*c8dee2aaSAndroid Build Coastguard Worker     } else {
3878*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(c.fPosition, "unsupported compound constructor");
3879*c8dee2aaSAndroid Build Coastguard Worker         return {};
3880*c8dee2aaSAndroid Build Coastguard Worker     }
3881*c8dee2aaSAndroid Build Coastguard Worker }
3882*c8dee2aaSAndroid Build Coastguard Worker 
assembleConstructorCompoundVector(const ConstructorCompound & c)3883*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleConstructorCompoundVector(const ConstructorCompound& c) {
3884*c8dee2aaSAndroid Build Coastguard Worker     // WGSL supports constructing vectors from a mix of scalars and vectors but
3885*c8dee2aaSAndroid Build Coastguard Worker     // not matrices (see https://www.w3.org/TR/WGSL/#type-constructor-expr).
3886*c8dee2aaSAndroid Build Coastguard Worker     //
3887*c8dee2aaSAndroid Build Coastguard Worker     // SkSL supports vec4(mat2x2) which we handle specially.
3888*c8dee2aaSAndroid Build Coastguard Worker     if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
3889*c8dee2aaSAndroid Build Coastguard Worker         const Expression& arg = *c.argumentSpan().front();
3890*c8dee2aaSAndroid Build Coastguard Worker         if (arg.type().isMatrix()) {
3891*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg.type().columns() == 2);
3892*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg.type().rows() == 2);
3893*c8dee2aaSAndroid Build Coastguard Worker 
3894*c8dee2aaSAndroid Build Coastguard Worker             std::string matrix = this->writeNontrivialScratchLet(arg, Precedence::kPostfix);
3895*c8dee2aaSAndroid Build Coastguard Worker             return String::printf("%s(%s[0], %s[1])", to_wgsl_type(fContext, c.type()).c_str(),
3896*c8dee2aaSAndroid Build Coastguard Worker                                                       matrix.c_str(),
3897*c8dee2aaSAndroid Build Coastguard Worker                                                       matrix.c_str());
3898*c8dee2aaSAndroid Build Coastguard Worker         }
3899*c8dee2aaSAndroid Build Coastguard Worker     }
3900*c8dee2aaSAndroid Build Coastguard Worker     return this->assembleAnyConstructor(c);
3901*c8dee2aaSAndroid Build Coastguard Worker }
3902*c8dee2aaSAndroid Build Coastguard Worker 
assembleConstructorCompoundMatrix(const ConstructorCompound & ctor)3903*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleConstructorCompoundMatrix(const ConstructorCompound& ctor) {
3904*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(ctor.type().isMatrix());
3905*c8dee2aaSAndroid Build Coastguard Worker 
3906*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, ctor.type()) + '(';
3907*c8dee2aaSAndroid Build Coastguard Worker     auto separator = String::Separator();
3908*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Expression>& arg : ctor.arguments()) {
3909*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(arg->type().isScalar() || arg->type().isVector());
3910*c8dee2aaSAndroid Build Coastguard Worker 
3911*c8dee2aaSAndroid Build Coastguard Worker         if (arg->type().isScalar()) {
3912*c8dee2aaSAndroid Build Coastguard Worker             expr += separator();
3913*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleExpression(*arg, Precedence::kSequence);
3914*c8dee2aaSAndroid Build Coastguard Worker         } else {
3915*c8dee2aaSAndroid Build Coastguard Worker             std::string inner = this->writeNontrivialScratchLet(*arg, Precedence::kSequence);
3916*c8dee2aaSAndroid Build Coastguard Worker             int numSlots = arg->type().slotCount();
3917*c8dee2aaSAndroid Build Coastguard Worker             for (int slot = 0; slot < numSlots; ++slot) {
3918*c8dee2aaSAndroid Build Coastguard Worker                 String::appendf(&expr, "%s%s[%d]", separator().c_str(), inner.c_str(), slot);
3919*c8dee2aaSAndroid Build Coastguard Worker             }
3920*c8dee2aaSAndroid Build Coastguard Worker         }
3921*c8dee2aaSAndroid Build Coastguard Worker     }
3922*c8dee2aaSAndroid Build Coastguard Worker     return expr + ')';
3923*c8dee2aaSAndroid Build Coastguard Worker }
3924*c8dee2aaSAndroid Build Coastguard Worker 
assembleConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & c)3925*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleConstructorDiagonalMatrix(
3926*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorDiagonalMatrix& c) {
3927*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = c.type();
3928*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isMatrix());
3929*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(c.argument()->type().isScalar());
3930*c8dee2aaSAndroid Build Coastguard Worker 
3931*c8dee2aaSAndroid Build Coastguard Worker     // Evaluate the inner-expression, creating a scratch variable if necessary.
3932*c8dee2aaSAndroid Build Coastguard Worker     std::string inner = this->writeNontrivialScratchLet(*c.argument(), Precedence::kAssignment);
3933*c8dee2aaSAndroid Build Coastguard Worker 
3934*c8dee2aaSAndroid Build Coastguard Worker     // Assemble a diagonal-matrix expression.
3935*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, type) + '(';
3936*c8dee2aaSAndroid Build Coastguard Worker     auto separator = String::Separator();
3937*c8dee2aaSAndroid Build Coastguard Worker     for (int col = 0; col < type.columns(); ++col) {
3938*c8dee2aaSAndroid Build Coastguard Worker         for (int row = 0; row < type.rows(); ++row) {
3939*c8dee2aaSAndroid Build Coastguard Worker             expr += separator();
3940*c8dee2aaSAndroid Build Coastguard Worker             if (col == row) {
3941*c8dee2aaSAndroid Build Coastguard Worker                 expr += inner;
3942*c8dee2aaSAndroid Build Coastguard Worker             } else {
3943*c8dee2aaSAndroid Build Coastguard Worker                 expr += "0.0";
3944*c8dee2aaSAndroid Build Coastguard Worker             }
3945*c8dee2aaSAndroid Build Coastguard Worker         }
3946*c8dee2aaSAndroid Build Coastguard Worker     }
3947*c8dee2aaSAndroid Build Coastguard Worker     return expr + ')';
3948*c8dee2aaSAndroid Build Coastguard Worker }
3949*c8dee2aaSAndroid Build Coastguard Worker 
assembleConstructorMatrixResize(const ConstructorMatrixResize & ctor)3950*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleConstructorMatrixResize(
3951*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorMatrixResize& ctor) {
3952*c8dee2aaSAndroid Build Coastguard Worker     std::string source = this->writeNontrivialScratchLet(*ctor.argument(), Precedence::kSequence);
3953*c8dee2aaSAndroid Build Coastguard Worker     int columns = ctor.type().columns();
3954*c8dee2aaSAndroid Build Coastguard Worker     int rows = ctor.type().rows();
3955*c8dee2aaSAndroid Build Coastguard Worker     int sourceColumns = ctor.argument()->type().columns();
3956*c8dee2aaSAndroid Build Coastguard Worker     int sourceRows = ctor.argument()->type().rows();
3957*c8dee2aaSAndroid Build Coastguard Worker     auto separator = String::Separator();
3958*c8dee2aaSAndroid Build Coastguard Worker     std::string expr = to_wgsl_type(fContext, ctor.type()) + '(';
3959*c8dee2aaSAndroid Build Coastguard Worker 
3960*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < columns; ++c) {
3961*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < rows; ++r) {
3962*c8dee2aaSAndroid Build Coastguard Worker             expr += separator();
3963*c8dee2aaSAndroid Build Coastguard Worker             if (c < sourceColumns && r < sourceRows) {
3964*c8dee2aaSAndroid Build Coastguard Worker                 String::appendf(&expr, "%s[%d][%d]", source.c_str(), c, r);
3965*c8dee2aaSAndroid Build Coastguard Worker             } else if (r == c) {
3966*c8dee2aaSAndroid Build Coastguard Worker                 expr += "1.0";
3967*c8dee2aaSAndroid Build Coastguard Worker             } else {
3968*c8dee2aaSAndroid Build Coastguard Worker                 expr += "0.0";
3969*c8dee2aaSAndroid Build Coastguard Worker             }
3970*c8dee2aaSAndroid Build Coastguard Worker         }
3971*c8dee2aaSAndroid Build Coastguard Worker     }
3972*c8dee2aaSAndroid Build Coastguard Worker 
3973*c8dee2aaSAndroid Build Coastguard Worker     return expr + ')';
3974*c8dee2aaSAndroid Build Coastguard Worker }
3975*c8dee2aaSAndroid Build Coastguard Worker 
assembleEqualityExpression(const Type & left,const std::string & leftName,const Type & right,const std::string & rightName,Operator op,Precedence parentPrecedence)3976*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleEqualityExpression(const Type& left,
3977*c8dee2aaSAndroid Build Coastguard Worker                                                           const std::string& leftName,
3978*c8dee2aaSAndroid Build Coastguard Worker                                                           const Type& right,
3979*c8dee2aaSAndroid Build Coastguard Worker                                                           const std::string& rightName,
3980*c8dee2aaSAndroid Build Coastguard Worker                                                           Operator op,
3981*c8dee2aaSAndroid Build Coastguard Worker                                                           Precedence parentPrecedence) {
3982*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op.kind() == OperatorKind::EQEQ || op.kind() == OperatorKind::NEQ);
3983*c8dee2aaSAndroid Build Coastguard Worker 
3984*c8dee2aaSAndroid Build Coastguard Worker     std::string expr;
3985*c8dee2aaSAndroid Build Coastguard Worker     bool isEqual = (op.kind() == Operator::Kind::EQEQ);
3986*c8dee2aaSAndroid Build Coastguard Worker     const char* const combiner = isEqual ? " && " : " || ";
3987*c8dee2aaSAndroid Build Coastguard Worker 
3988*c8dee2aaSAndroid Build Coastguard Worker     if (left.isMatrix()) {
3989*c8dee2aaSAndroid Build Coastguard Worker         // Each matrix column must be compared as if it were an individual vector.
3990*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(right.isMatrix());
3991*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(left.rows() == right.rows());
3992*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(left.columns() == right.columns());
3993*c8dee2aaSAndroid Build Coastguard Worker         int columns = left.columns();
3994*c8dee2aaSAndroid Build Coastguard Worker         const Type& vecType = left.columnType(fContext);
3995*c8dee2aaSAndroid Build Coastguard Worker         const char* separator = "(";
3996*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < columns; ++index) {
3997*c8dee2aaSAndroid Build Coastguard Worker             expr += separator;
3998*c8dee2aaSAndroid Build Coastguard Worker             std::string suffix = '[' + std::to_string(index) + ']';
3999*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleEqualityExpression(vecType, leftName + suffix,
4000*c8dee2aaSAndroid Build Coastguard Worker                                                      vecType, rightName + suffix,
4001*c8dee2aaSAndroid Build Coastguard Worker                                                      op, Precedence::kParentheses);
4002*c8dee2aaSAndroid Build Coastguard Worker             separator = combiner;
4003*c8dee2aaSAndroid Build Coastguard Worker         }
4004*c8dee2aaSAndroid Build Coastguard Worker         return expr + ')';
4005*c8dee2aaSAndroid Build Coastguard Worker     }
4006*c8dee2aaSAndroid Build Coastguard Worker 
4007*c8dee2aaSAndroid Build Coastguard Worker     if (left.isArray()) {
4008*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(right.matches(left));
4009*c8dee2aaSAndroid Build Coastguard Worker         const Type& indexedType = left.componentType();
4010*c8dee2aaSAndroid Build Coastguard Worker         const char* separator = "(";
4011*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < left.columns(); ++index) {
4012*c8dee2aaSAndroid Build Coastguard Worker             expr += separator;
4013*c8dee2aaSAndroid Build Coastguard Worker             std::string suffix = '[' + std::to_string(index) + ']';
4014*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleEqualityExpression(indexedType, leftName + suffix,
4015*c8dee2aaSAndroid Build Coastguard Worker                                                      indexedType, rightName + suffix,
4016*c8dee2aaSAndroid Build Coastguard Worker                                                      op, Precedence::kParentheses);
4017*c8dee2aaSAndroid Build Coastguard Worker             separator = combiner;
4018*c8dee2aaSAndroid Build Coastguard Worker         }
4019*c8dee2aaSAndroid Build Coastguard Worker         return expr + ')';
4020*c8dee2aaSAndroid Build Coastguard Worker     }
4021*c8dee2aaSAndroid Build Coastguard Worker 
4022*c8dee2aaSAndroid Build Coastguard Worker     if (left.isStruct()) {
4023*c8dee2aaSAndroid Build Coastguard Worker         // Recursively compare every field in the struct.
4024*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(right.matches(left));
4025*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const Field> fields = left.fields();
4026*c8dee2aaSAndroid Build Coastguard Worker 
4027*c8dee2aaSAndroid Build Coastguard Worker         const char* separator = "(";
4028*c8dee2aaSAndroid Build Coastguard Worker         for (const Field& field : fields) {
4029*c8dee2aaSAndroid Build Coastguard Worker             expr += separator;
4030*c8dee2aaSAndroid Build Coastguard Worker             expr += this->assembleEqualityExpression(
4031*c8dee2aaSAndroid Build Coastguard Worker                             *field.fType, leftName + '.' + this->assembleName(field.fName),
4032*c8dee2aaSAndroid Build Coastguard Worker                             *field.fType, rightName + '.' + this->assembleName(field.fName),
4033*c8dee2aaSAndroid Build Coastguard Worker                             op, Precedence::kParentheses);
4034*c8dee2aaSAndroid Build Coastguard Worker             separator = combiner;
4035*c8dee2aaSAndroid Build Coastguard Worker         }
4036*c8dee2aaSAndroid Build Coastguard Worker         return expr + ')';
4037*c8dee2aaSAndroid Build Coastguard Worker     }
4038*c8dee2aaSAndroid Build Coastguard Worker 
4039*c8dee2aaSAndroid Build Coastguard Worker     if (left.isVector()) {
4040*c8dee2aaSAndroid Build Coastguard Worker         // Compare vectors via `all(x == y)` or `any(x != y)`.
4041*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(right.isVector());
4042*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(left.slotCount() == right.slotCount());
4043*c8dee2aaSAndroid Build Coastguard Worker 
4044*c8dee2aaSAndroid Build Coastguard Worker         expr += isEqual ? "all(" : "any(";
4045*c8dee2aaSAndroid Build Coastguard Worker         expr += leftName;
4046*c8dee2aaSAndroid Build Coastguard Worker         expr += operator_name(op);
4047*c8dee2aaSAndroid Build Coastguard Worker         expr += rightName;
4048*c8dee2aaSAndroid Build Coastguard Worker         return expr + ')';
4049*c8dee2aaSAndroid Build Coastguard Worker     }
4050*c8dee2aaSAndroid Build Coastguard Worker 
4051*c8dee2aaSAndroid Build Coastguard Worker     // Compare scalars via `x == y`.
4052*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(right.isScalar());
4053*c8dee2aaSAndroid Build Coastguard Worker     if (parentPrecedence < Precedence::kSequence) {
4054*c8dee2aaSAndroid Build Coastguard Worker         expr = '(';
4055*c8dee2aaSAndroid Build Coastguard Worker     }
4056*c8dee2aaSAndroid Build Coastguard Worker     expr += leftName;
4057*c8dee2aaSAndroid Build Coastguard Worker     expr += operator_name(op);
4058*c8dee2aaSAndroid Build Coastguard Worker     expr += rightName;
4059*c8dee2aaSAndroid Build Coastguard Worker     if (parentPrecedence < Precedence::kSequence) {
4060*c8dee2aaSAndroid Build Coastguard Worker         expr += ')';
4061*c8dee2aaSAndroid Build Coastguard Worker     }
4062*c8dee2aaSAndroid Build Coastguard Worker     return expr;
4063*c8dee2aaSAndroid Build Coastguard Worker }
4064*c8dee2aaSAndroid Build Coastguard Worker 
assembleEqualityExpression(const Expression & left,const Expression & right,Operator op,Precedence parentPrecedence)4065*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::assembleEqualityExpression(const Expression& left,
4066*c8dee2aaSAndroid Build Coastguard Worker                                                           const Expression& right,
4067*c8dee2aaSAndroid Build Coastguard Worker                                                           Operator op,
4068*c8dee2aaSAndroid Build Coastguard Worker                                                           Precedence parentPrecedence) {
4069*c8dee2aaSAndroid Build Coastguard Worker     std::string leftName, rightName;
4070*c8dee2aaSAndroid Build Coastguard Worker     if (left.type().isScalar() || left.type().isVector()) {
4071*c8dee2aaSAndroid Build Coastguard Worker         // WGSL supports scalar and vector comparisons natively. We know the expressions will only
4072*c8dee2aaSAndroid Build Coastguard Worker         // be emitted once, so there isn't a benefit to creating a let-declaration.
4073*c8dee2aaSAndroid Build Coastguard Worker         leftName = this->assembleExpression(left, Precedence::kParentheses);
4074*c8dee2aaSAndroid Build Coastguard Worker         rightName = this->assembleExpression(right, Precedence::kParentheses);
4075*c8dee2aaSAndroid Build Coastguard Worker     } else {
4076*c8dee2aaSAndroid Build Coastguard Worker         leftName = this->writeNontrivialScratchLet(left, Precedence::kAssignment);
4077*c8dee2aaSAndroid Build Coastguard Worker         rightName = this->writeNontrivialScratchLet(right, Precedence::kAssignment);
4078*c8dee2aaSAndroid Build Coastguard Worker     }
4079*c8dee2aaSAndroid Build Coastguard Worker     return this->assembleEqualityExpression(left.type(), leftName, right.type(), rightName,
4080*c8dee2aaSAndroid Build Coastguard Worker                                             op, parentPrecedence);
4081*c8dee2aaSAndroid Build Coastguard Worker }
4082*c8dee2aaSAndroid Build Coastguard Worker 
writeProgramElement(const ProgramElement & e)4083*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeProgramElement(const ProgramElement& e) {
4084*c8dee2aaSAndroid Build Coastguard Worker     switch (e.kind()) {
4085*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kExtension:
4086*c8dee2aaSAndroid Build Coastguard Worker             // TODO(skia:13092): WGSL supports extensions via the "enable" directive
4087*c8dee2aaSAndroid Build Coastguard Worker             // (https://www.w3.org/TR/WGSL/#enable-extensions-sec ). While we could easily emit this
4088*c8dee2aaSAndroid Build Coastguard Worker             // directive, we should first ensure that all possible SkSL extension names are
4089*c8dee2aaSAndroid Build Coastguard Worker             // converted to their appropriate WGSL extension.
4090*c8dee2aaSAndroid Build Coastguard Worker             break;
4091*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kGlobalVar:
4092*c8dee2aaSAndroid Build Coastguard Worker             this->writeGlobalVarDeclaration(e.as<GlobalVarDeclaration>());
4093*c8dee2aaSAndroid Build Coastguard Worker             break;
4094*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kInterfaceBlock:
4095*c8dee2aaSAndroid Build Coastguard Worker             // All interface block declarations are handled explicitly as the "program header" in
4096*c8dee2aaSAndroid Build Coastguard Worker             // generateCode().
4097*c8dee2aaSAndroid Build Coastguard Worker             break;
4098*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kStructDefinition:
4099*c8dee2aaSAndroid Build Coastguard Worker             this->writeStructDefinition(e.as<StructDefinition>());
4100*c8dee2aaSAndroid Build Coastguard Worker             break;
4101*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kFunctionPrototype:
4102*c8dee2aaSAndroid Build Coastguard Worker             // A WGSL function declaration must contain its body and the function name is in scope
4103*c8dee2aaSAndroid Build Coastguard Worker             // for the entire program (see https://www.w3.org/TR/WGSL/#function-declaration and
4104*c8dee2aaSAndroid Build Coastguard Worker             // https://www.w3.org/TR/WGSL/#declaration-and-scope).
4105*c8dee2aaSAndroid Build Coastguard Worker             //
4106*c8dee2aaSAndroid Build Coastguard Worker             // As such, we don't emit function prototypes.
4107*c8dee2aaSAndroid Build Coastguard Worker             break;
4108*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kFunction:
4109*c8dee2aaSAndroid Build Coastguard Worker             this->writeFunction(e.as<FunctionDefinition>());
4110*c8dee2aaSAndroid Build Coastguard Worker             break;
4111*c8dee2aaSAndroid Build Coastguard Worker         case ProgramElement::Kind::kModifiers:
4112*c8dee2aaSAndroid Build Coastguard Worker             this->writeModifiersDeclaration(e.as<ModifiersDeclaration>());
4113*c8dee2aaSAndroid Build Coastguard Worker             break;
4114*c8dee2aaSAndroid Build Coastguard Worker         default:
4115*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported program element: %s\n", e.description().c_str());
4116*c8dee2aaSAndroid Build Coastguard Worker             break;
4117*c8dee2aaSAndroid Build Coastguard Worker     }
4118*c8dee2aaSAndroid Build Coastguard Worker }
4119*c8dee2aaSAndroid Build Coastguard Worker 
writeTextureOrSampler(const Variable & var,int bindingLocation,std::string_view suffix,std::string_view wgslType)4120*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeTextureOrSampler(const Variable& var,
4121*c8dee2aaSAndroid Build Coastguard Worker                                               int bindingLocation,
4122*c8dee2aaSAndroid Build Coastguard Worker                                               std::string_view suffix,
4123*c8dee2aaSAndroid Build Coastguard Worker                                               std::string_view wgslType) {
4124*c8dee2aaSAndroid Build Coastguard Worker     if (var.type().dimensions() != SpvDim2D) {
4125*c8dee2aaSAndroid Build Coastguard Worker         // Skia currently only uses 2D textures.
4126*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(var.varDeclaration()->position(), "unsupported texture dimensions");
4127*c8dee2aaSAndroid Build Coastguard Worker         return;
4128*c8dee2aaSAndroid Build Coastguard Worker     }
4129*c8dee2aaSAndroid Build Coastguard Worker 
4130*c8dee2aaSAndroid Build Coastguard Worker     this->write("@group(");
4131*c8dee2aaSAndroid Build Coastguard Worker     this->write(std::to_string(std::max(0, var.layout().fSet)));
4132*c8dee2aaSAndroid Build Coastguard Worker     this->write(") @binding(");
4133*c8dee2aaSAndroid Build Coastguard Worker     this->write(std::to_string(bindingLocation));
4134*c8dee2aaSAndroid Build Coastguard Worker     this->write(") var ");
4135*c8dee2aaSAndroid Build Coastguard Worker     this->write(this->assembleName(var.mangledName()));
4136*c8dee2aaSAndroid Build Coastguard Worker     this->write(suffix);
4137*c8dee2aaSAndroid Build Coastguard Worker     this->write(": ");
4138*c8dee2aaSAndroid Build Coastguard Worker     this->write(wgslType);
4139*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(";");
4140*c8dee2aaSAndroid Build Coastguard Worker }
4141*c8dee2aaSAndroid Build Coastguard Worker 
writeGlobalVarDeclaration(const GlobalVarDeclaration & d)4142*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeGlobalVarDeclaration(const GlobalVarDeclaration& d) {
4143*c8dee2aaSAndroid Build Coastguard Worker     const VarDeclaration& decl = d.varDeclaration();
4144*c8dee2aaSAndroid Build Coastguard Worker     const Variable& var = *decl.var();
4145*c8dee2aaSAndroid Build Coastguard Worker     if ((var.modifierFlags() & (ModifierFlag::kIn | ModifierFlag::kOut)) ||
4146*c8dee2aaSAndroid Build Coastguard Worker         is_in_global_uniforms(var)) {
4147*c8dee2aaSAndroid Build Coastguard Worker         // Pipeline stage I/O parameters and top-level (non-block) uniforms are handled specially
4148*c8dee2aaSAndroid Build Coastguard Worker         // in generateCode().
4149*c8dee2aaSAndroid Build Coastguard Worker         return;
4150*c8dee2aaSAndroid Build Coastguard Worker     }
4151*c8dee2aaSAndroid Build Coastguard Worker 
4152*c8dee2aaSAndroid Build Coastguard Worker     const Type::TypeKind varKind = var.type().typeKind();
4153*c8dee2aaSAndroid Build Coastguard Worker     if (varKind == Type::TypeKind::kSampler) {
4154*c8dee2aaSAndroid Build Coastguard Worker         // If the sampler binding was unassigned, provide a scratch value; this will make
4155*c8dee2aaSAndroid Build Coastguard Worker         // golden-output tests pass, but will not actually be usable for drawing.
4156*c8dee2aaSAndroid Build Coastguard Worker         int samplerLocation = var.layout().fSampler >= 0 ? var.layout().fSampler
4157*c8dee2aaSAndroid Build Coastguard Worker                                                          : 10000 + fScratchCount++;
4158*c8dee2aaSAndroid Build Coastguard Worker         this->writeTextureOrSampler(var, samplerLocation, kSamplerSuffix, "sampler");
4159*c8dee2aaSAndroid Build Coastguard Worker 
4160*c8dee2aaSAndroid Build Coastguard Worker         // If the texture binding was unassigned, provide a scratch value (for golden-output tests).
4161*c8dee2aaSAndroid Build Coastguard Worker         int textureLocation = var.layout().fTexture >= 0 ? var.layout().fTexture
4162*c8dee2aaSAndroid Build Coastguard Worker                                                          : 10000 + fScratchCount++;
4163*c8dee2aaSAndroid Build Coastguard Worker         this->writeTextureOrSampler(var, textureLocation, kTextureSuffix, "texture_2d<f32>");
4164*c8dee2aaSAndroid Build Coastguard Worker         return;
4165*c8dee2aaSAndroid Build Coastguard Worker     }
4166*c8dee2aaSAndroid Build Coastguard Worker 
4167*c8dee2aaSAndroid Build Coastguard Worker     if (varKind == Type::TypeKind::kTexture) {
4168*c8dee2aaSAndroid Build Coastguard Worker         // If a binding location was unassigned, provide a scratch value (for golden-output tests).
4169*c8dee2aaSAndroid Build Coastguard Worker         int textureLocation = var.layout().fBinding >= 0 ? var.layout().fBinding
4170*c8dee2aaSAndroid Build Coastguard Worker                                                          : 10000 + fScratchCount++;
4171*c8dee2aaSAndroid Build Coastguard Worker         // For a texture without an associated sampler, we don't apply a suffix.
4172*c8dee2aaSAndroid Build Coastguard Worker         this->writeTextureOrSampler(var, textureLocation, /*suffix=*/"",
4173*c8dee2aaSAndroid Build Coastguard Worker                                     to_wgsl_type(fContext, var.type(), &var.layout()));
4174*c8dee2aaSAndroid Build Coastguard Worker         return;
4175*c8dee2aaSAndroid Build Coastguard Worker     }
4176*c8dee2aaSAndroid Build Coastguard Worker 
4177*c8dee2aaSAndroid Build Coastguard Worker     std::string initializer;
4178*c8dee2aaSAndroid Build Coastguard Worker     if (decl.value()) {
4179*c8dee2aaSAndroid Build Coastguard Worker         // We assume here that the initial-value expression will not emit any helper statements.
4180*c8dee2aaSAndroid Build Coastguard Worker         // Initial-value expressions are required to pass IsConstantExpression, which limits the
4181*c8dee2aaSAndroid Build Coastguard Worker         // blast radius to constructors, literals, and other constant values/variables.
4182*c8dee2aaSAndroid Build Coastguard Worker         initializer += " = ";
4183*c8dee2aaSAndroid Build Coastguard Worker         initializer += this->assembleExpression(*decl.value(), Precedence::kAssignment);
4184*c8dee2aaSAndroid Build Coastguard Worker     }
4185*c8dee2aaSAndroid Build Coastguard Worker 
4186*c8dee2aaSAndroid Build Coastguard Worker     if (var.modifierFlags().isConst()) {
4187*c8dee2aaSAndroid Build Coastguard Worker         this->write("const ");
4188*c8dee2aaSAndroid Build Coastguard Worker     } else if (var.modifierFlags().isWorkgroup()) {
4189*c8dee2aaSAndroid Build Coastguard Worker         this->write("var<workgroup> ");
4190*c8dee2aaSAndroid Build Coastguard Worker     } else if (var.modifierFlags().isPixelLocal()) {
4191*c8dee2aaSAndroid Build Coastguard Worker         this->write("var<pixel_local> ");
4192*c8dee2aaSAndroid Build Coastguard Worker     } else {
4193*c8dee2aaSAndroid Build Coastguard Worker         this->write("var<private> ");
4194*c8dee2aaSAndroid Build Coastguard Worker     }
4195*c8dee2aaSAndroid Build Coastguard Worker     this->write(this->assembleName(var.mangledName()));
4196*c8dee2aaSAndroid Build Coastguard Worker     this->write(": " + to_wgsl_type(fContext, var.type(), &var.layout()));
4197*c8dee2aaSAndroid Build Coastguard Worker     this->write(initializer);
4198*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine(";");
4199*c8dee2aaSAndroid Build Coastguard Worker }
4200*c8dee2aaSAndroid Build Coastguard Worker 
writeStructDefinition(const StructDefinition & s)4201*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeStructDefinition(const StructDefinition& s) {
4202*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = s.type();
4203*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("struct " + type.displayName() + " {");
4204*c8dee2aaSAndroid Build Coastguard Worker     this->writeFields(type.fields(), /*memoryLayout=*/nullptr);
4205*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("};");
4206*c8dee2aaSAndroid Build Coastguard Worker }
4207*c8dee2aaSAndroid Build Coastguard Worker 
writeModifiersDeclaration(const ModifiersDeclaration & modifiers)4208*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeModifiersDeclaration(const ModifiersDeclaration& modifiers) {
4209*c8dee2aaSAndroid Build Coastguard Worker     LayoutFlags flags = modifiers.layout().fFlags;
4210*c8dee2aaSAndroid Build Coastguard Worker     flags &= ~(LayoutFlag::kLocalSizeX | LayoutFlag::kLocalSizeY | LayoutFlag::kLocalSizeZ);
4211*c8dee2aaSAndroid Build Coastguard Worker     if (flags != LayoutFlag::kNone) {
4212*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(modifiers.position(), "unsupported declaration");
4213*c8dee2aaSAndroid Build Coastguard Worker         return;
4214*c8dee2aaSAndroid Build Coastguard Worker     }
4215*c8dee2aaSAndroid Build Coastguard Worker 
4216*c8dee2aaSAndroid Build Coastguard Worker     if (modifiers.layout().fLocalSizeX >= 0) {
4217*c8dee2aaSAndroid Build Coastguard Worker         fLocalSizeX = modifiers.layout().fLocalSizeX;
4218*c8dee2aaSAndroid Build Coastguard Worker     }
4219*c8dee2aaSAndroid Build Coastguard Worker     if (modifiers.layout().fLocalSizeY >= 0) {
4220*c8dee2aaSAndroid Build Coastguard Worker         fLocalSizeY = modifiers.layout().fLocalSizeY;
4221*c8dee2aaSAndroid Build Coastguard Worker     }
4222*c8dee2aaSAndroid Build Coastguard Worker     if (modifiers.layout().fLocalSizeZ >= 0) {
4223*c8dee2aaSAndroid Build Coastguard Worker         fLocalSizeZ = modifiers.layout().fLocalSizeZ;
4224*c8dee2aaSAndroid Build Coastguard Worker     }
4225*c8dee2aaSAndroid Build Coastguard Worker }
4226*c8dee2aaSAndroid Build Coastguard Worker 
writeFields(SkSpan<const Field> fields,const MemoryLayout * memoryLayout)4227*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeFields(SkSpan<const Field> fields, const MemoryLayout* memoryLayout) {
4228*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
4229*c8dee2aaSAndroid Build Coastguard Worker 
4230*c8dee2aaSAndroid Build Coastguard Worker     // TODO(skia:14370): array uniforms may need manual fixup for std140 padding. (Those uniforms
4231*c8dee2aaSAndroid Build Coastguard Worker     // will also need special handling when they are accessed, or passed to functions.)
4232*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < fields.size(); ++index) {
4233*c8dee2aaSAndroid Build Coastguard Worker         const Field& field = fields[index];
4234*c8dee2aaSAndroid Build Coastguard Worker         if (memoryLayout && !memoryLayout->isSupported(*field.fType)) {
4235*c8dee2aaSAndroid Build Coastguard Worker             // Reject types that aren't supported by the memory layout.
4236*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(field.fPosition, "type '" + std::string(field.fType->name()) +
4237*c8dee2aaSAndroid Build Coastguard Worker                                                      "' is not permitted here");
4238*c8dee2aaSAndroid Build Coastguard Worker             return;
4239*c8dee2aaSAndroid Build Coastguard Worker         }
4240*c8dee2aaSAndroid Build Coastguard Worker 
4241*c8dee2aaSAndroid Build Coastguard Worker         // Prepend @size(n) to enforce the offsets from the SkSL layout. (This is effectively
4242*c8dee2aaSAndroid Build Coastguard Worker         // a gadget that we can use to insert padding between elements.)
4243*c8dee2aaSAndroid Build Coastguard Worker         if (index < fields.size() - 1) {
4244*c8dee2aaSAndroid Build Coastguard Worker             int thisFieldOffset = field.fLayout.fOffset;
4245*c8dee2aaSAndroid Build Coastguard Worker             int nextFieldOffset = fields[index + 1].fLayout.fOffset;
4246*c8dee2aaSAndroid Build Coastguard Worker             if (index == 0 && thisFieldOffset > 0) {
4247*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(field.fPosition, "field must have an offset of zero");
4248*c8dee2aaSAndroid Build Coastguard Worker                 return;
4249*c8dee2aaSAndroid Build Coastguard Worker             }
4250*c8dee2aaSAndroid Build Coastguard Worker             if (thisFieldOffset >= 0 && nextFieldOffset > thisFieldOffset) {
4251*c8dee2aaSAndroid Build Coastguard Worker                 this->write("@size(");
4252*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(nextFieldOffset - thisFieldOffset));
4253*c8dee2aaSAndroid Build Coastguard Worker                 this->write(") ");
4254*c8dee2aaSAndroid Build Coastguard Worker             }
4255*c8dee2aaSAndroid Build Coastguard Worker         }
4256*c8dee2aaSAndroid Build Coastguard Worker 
4257*c8dee2aaSAndroid Build Coastguard Worker         this->write(this->assembleName(field.fName));
4258*c8dee2aaSAndroid Build Coastguard Worker         this->write(": ");
4259*c8dee2aaSAndroid Build Coastguard Worker         if (const FieldPolyfillInfo* info = fFieldPolyfillMap.find(&field)) {
4260*c8dee2aaSAndroid Build Coastguard Worker             if (info->fIsArray) {
4261*c8dee2aaSAndroid Build Coastguard Worker                 // This properly handles arrays of matrices, as well as arrays of other primitives.
4262*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(field.fType->isArray());
4263*c8dee2aaSAndroid Build Coastguard Worker                 this->write("array<_skArrayElement_");
4264*c8dee2aaSAndroid Build Coastguard Worker                 this->write(field.fType->abbreviatedName());
4265*c8dee2aaSAndroid Build Coastguard Worker                 this->write(", ");
4266*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(field.fType->columns()));
4267*c8dee2aaSAndroid Build Coastguard Worker                 this->write(">");
4268*c8dee2aaSAndroid Build Coastguard Worker             } else if (info->fIsMatrix) {
4269*c8dee2aaSAndroid Build Coastguard Worker                 this->write("_skMatrix");
4270*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(field.fType->columns()));
4271*c8dee2aaSAndroid Build Coastguard Worker                 this->write(std::to_string(field.fType->rows()));
4272*c8dee2aaSAndroid Build Coastguard Worker             } else {
4273*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("need polyfill for %s", info->fReplacementName.c_str());
4274*c8dee2aaSAndroid Build Coastguard Worker             }
4275*c8dee2aaSAndroid Build Coastguard Worker         } else {
4276*c8dee2aaSAndroid Build Coastguard Worker             this->write(to_wgsl_type(fContext, *field.fType, &field.fLayout));
4277*c8dee2aaSAndroid Build Coastguard Worker         }
4278*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(",");
4279*c8dee2aaSAndroid Build Coastguard Worker     }
4280*c8dee2aaSAndroid Build Coastguard Worker 
4281*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
4282*c8dee2aaSAndroid Build Coastguard Worker }
4283*c8dee2aaSAndroid Build Coastguard Worker 
writeEnables()4284*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeEnables() {
4285*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("diagnostic(off, derivative_uniformity);");
4286*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("diagnostic(off, chromium.unreachable_code);");
4287*c8dee2aaSAndroid Build Coastguard Worker 
4288*c8dee2aaSAndroid Build Coastguard Worker     if (fRequirements.fPixelLocalExtension) {
4289*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("enable chromium_experimental_pixel_local;");
4290*c8dee2aaSAndroid Build Coastguard Worker     }
4291*c8dee2aaSAndroid Build Coastguard Worker     if (fProgram.fInterface.fUseLastFragColor) {
4292*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("enable chromium_experimental_framebuffer_fetch;");
4293*c8dee2aaSAndroid Build Coastguard Worker     }
4294*c8dee2aaSAndroid Build Coastguard Worker     if (fProgram.fInterface.fOutputSecondaryColor) {
4295*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("enable dual_source_blending;");
4296*c8dee2aaSAndroid Build Coastguard Worker     }
4297*c8dee2aaSAndroid Build Coastguard Worker }
4298*c8dee2aaSAndroid Build Coastguard Worker 
needsStageInputStruct() const4299*c8dee2aaSAndroid Build Coastguard Worker bool WGSLCodeGenerator::needsStageInputStruct() const {
4300*c8dee2aaSAndroid Build Coastguard Worker     // It is illegal to declare a struct with no members; we can't emit a placeholder empty stage
4301*c8dee2aaSAndroid Build Coastguard Worker     // input struct.
4302*c8dee2aaSAndroid Build Coastguard Worker     return !fPipelineInputs.empty();
4303*c8dee2aaSAndroid Build Coastguard Worker }
4304*c8dee2aaSAndroid Build Coastguard Worker 
writeStageInputStruct()4305*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeStageInputStruct() {
4306*c8dee2aaSAndroid Build Coastguard Worker     if (!this->needsStageInputStruct()) {
4307*c8dee2aaSAndroid Build Coastguard Worker         return;
4308*c8dee2aaSAndroid Build Coastguard Worker     }
4309*c8dee2aaSAndroid Build Coastguard Worker 
4310*c8dee2aaSAndroid Build Coastguard Worker     std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
4311*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!structNamePrefix.empty());
4312*c8dee2aaSAndroid Build Coastguard Worker 
4313*c8dee2aaSAndroid Build Coastguard Worker     this->write("struct ");
4314*c8dee2aaSAndroid Build Coastguard Worker     this->write(structNamePrefix);
4315*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("In {");
4316*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
4317*c8dee2aaSAndroid Build Coastguard Worker 
4318*c8dee2aaSAndroid Build Coastguard Worker     for (const Variable* v : fPipelineInputs) {
4319*c8dee2aaSAndroid Build Coastguard Worker         if (v->type().isInterfaceBlock()) {
4320*c8dee2aaSAndroid Build Coastguard Worker             for (const Field& f : v->type().fields()) {
4321*c8dee2aaSAndroid Build Coastguard Worker                 this->writePipelineIODeclaration(f.fLayout, *f.fType, f.fModifierFlags, f.fName,
4322*c8dee2aaSAndroid Build Coastguard Worker                                                  Delimiter::kComma);
4323*c8dee2aaSAndroid Build Coastguard Worker             }
4324*c8dee2aaSAndroid Build Coastguard Worker         } else {
4325*c8dee2aaSAndroid Build Coastguard Worker             this->writePipelineIODeclaration(v->layout(), v->type(), v->modifierFlags(),
4326*c8dee2aaSAndroid Build Coastguard Worker                                              v->mangledName(), Delimiter::kComma);
4327*c8dee2aaSAndroid Build Coastguard Worker         }
4328*c8dee2aaSAndroid Build Coastguard Worker     }
4329*c8dee2aaSAndroid Build Coastguard Worker 
4330*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
4331*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("};");
4332*c8dee2aaSAndroid Build Coastguard Worker }
4333*c8dee2aaSAndroid Build Coastguard Worker 
needsStageOutputStruct() const4334*c8dee2aaSAndroid Build Coastguard Worker bool WGSLCodeGenerator::needsStageOutputStruct() const {
4335*c8dee2aaSAndroid Build Coastguard Worker     // It is illegal to declare a struct with no members. However, vertex programs will _always_
4336*c8dee2aaSAndroid Build Coastguard Worker     // have an output stage in WGSL, because the spec requires them to emit `@builtin(position)`.
4337*c8dee2aaSAndroid Build Coastguard Worker     // So we always synthesize a reference to `sk_Position` even if the program doesn't need it.
4338*c8dee2aaSAndroid Build Coastguard Worker     return !fPipelineOutputs.empty() || ProgramConfig::IsVertex(fProgram.fConfig->fKind);
4339*c8dee2aaSAndroid Build Coastguard Worker }
4340*c8dee2aaSAndroid Build Coastguard Worker 
writeStageOutputStruct()4341*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeStageOutputStruct() {
4342*c8dee2aaSAndroid Build Coastguard Worker     if (!this->needsStageOutputStruct()) {
4343*c8dee2aaSAndroid Build Coastguard Worker         return;
4344*c8dee2aaSAndroid Build Coastguard Worker     }
4345*c8dee2aaSAndroid Build Coastguard Worker 
4346*c8dee2aaSAndroid Build Coastguard Worker     std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
4347*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!structNamePrefix.empty());
4348*c8dee2aaSAndroid Build Coastguard Worker 
4349*c8dee2aaSAndroid Build Coastguard Worker     this->write("struct ");
4350*c8dee2aaSAndroid Build Coastguard Worker     this->write(structNamePrefix);
4351*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("Out {");
4352*c8dee2aaSAndroid Build Coastguard Worker     fIndentation++;
4353*c8dee2aaSAndroid Build Coastguard Worker 
4354*c8dee2aaSAndroid Build Coastguard Worker     bool declaredPositionBuiltin = false;
4355*c8dee2aaSAndroid Build Coastguard Worker     bool requiresPointSizeBuiltin = false;
4356*c8dee2aaSAndroid Build Coastguard Worker     for (const Variable* v : fPipelineOutputs) {
4357*c8dee2aaSAndroid Build Coastguard Worker         if (v->type().isInterfaceBlock()) {
4358*c8dee2aaSAndroid Build Coastguard Worker             for (const auto& f : v->type().fields()) {
4359*c8dee2aaSAndroid Build Coastguard Worker                 this->writePipelineIODeclaration(f.fLayout, *f.fType, f.fModifierFlags, f.fName,
4360*c8dee2aaSAndroid Build Coastguard Worker                                                  Delimiter::kComma);
4361*c8dee2aaSAndroid Build Coastguard Worker                 if (f.fLayout.fBuiltin == SK_POSITION_BUILTIN) {
4362*c8dee2aaSAndroid Build Coastguard Worker                     declaredPositionBuiltin = true;
4363*c8dee2aaSAndroid Build Coastguard Worker                 } else if (f.fLayout.fBuiltin == SK_POINTSIZE_BUILTIN) {
4364*c8dee2aaSAndroid Build Coastguard Worker                     // sk_PointSize is explicitly not supported by `builtin_from_sksl_name` so
4365*c8dee2aaSAndroid Build Coastguard Worker                     // writePipelineIODeclaration will never write it. We mark it here if the
4366*c8dee2aaSAndroid Build Coastguard Worker                     // declaration is needed so we can synthesize it below.
4367*c8dee2aaSAndroid Build Coastguard Worker                     requiresPointSizeBuiltin = true;
4368*c8dee2aaSAndroid Build Coastguard Worker                 }
4369*c8dee2aaSAndroid Build Coastguard Worker             }
4370*c8dee2aaSAndroid Build Coastguard Worker         } else {
4371*c8dee2aaSAndroid Build Coastguard Worker             this->writePipelineIODeclaration(v->layout(), v->type(), v->modifierFlags(),
4372*c8dee2aaSAndroid Build Coastguard Worker                                              v->mangledName(), Delimiter::kComma);
4373*c8dee2aaSAndroid Build Coastguard Worker         }
4374*c8dee2aaSAndroid Build Coastguard Worker     }
4375*c8dee2aaSAndroid Build Coastguard Worker 
4376*c8dee2aaSAndroid Build Coastguard Worker     // A vertex program must include the `position` builtin in its entrypoint's return type.
4377*c8dee2aaSAndroid Build Coastguard Worker     const bool positionBuiltinRequired = ProgramConfig::IsVertex(fProgram.fConfig->fKind);
4378*c8dee2aaSAndroid Build Coastguard Worker     if (positionBuiltinRequired && !declaredPositionBuiltin) {
4379*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("@builtin(position) sk_Position: vec4<f32>,");
4380*c8dee2aaSAndroid Build Coastguard Worker     }
4381*c8dee2aaSAndroid Build Coastguard Worker 
4382*c8dee2aaSAndroid Build Coastguard Worker     fIndentation--;
4383*c8dee2aaSAndroid Build Coastguard Worker     this->writeLine("};");
4384*c8dee2aaSAndroid Build Coastguard Worker 
4385*c8dee2aaSAndroid Build Coastguard Worker     // In WebGPU/WGSL, the vertex stage does not support a point-size output and the size
4386*c8dee2aaSAndroid Build Coastguard Worker     // of a point primitive is always 1 pixel (see https://github.com/gpuweb/gpuweb/issues/332).
4387*c8dee2aaSAndroid Build Coastguard Worker     //
4388*c8dee2aaSAndroid Build Coastguard Worker     // There isn't anything we can do to emulate this correctly at this stage so we synthesize a
4389*c8dee2aaSAndroid Build Coastguard Worker     // placeholder global variable that has no effect. Programs should not rely on sk_PointSize when
4390*c8dee2aaSAndroid Build Coastguard Worker     // using the Dawn backend.
4391*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsVertex(fProgram.fConfig->fKind) && requiresPointSizeBuiltin) {
4392*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("/* unsupported */ var<private> sk_PointSize: f32;");
4393*c8dee2aaSAndroid Build Coastguard Worker     }
4394*c8dee2aaSAndroid Build Coastguard Worker }
4395*c8dee2aaSAndroid Build Coastguard Worker 
prepareUniformPolyfillsForInterfaceBlock(const InterfaceBlock * interfaceBlock,std::string_view instanceName,MemoryLayout::Standard nativeLayout)4396*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::prepareUniformPolyfillsForInterfaceBlock(
4397*c8dee2aaSAndroid Build Coastguard Worker         const InterfaceBlock* interfaceBlock,
4398*c8dee2aaSAndroid Build Coastguard Worker         std::string_view instanceName,
4399*c8dee2aaSAndroid Build Coastguard Worker         MemoryLayout::Standard nativeLayout) {
4400*c8dee2aaSAndroid Build Coastguard Worker     SkSL::MemoryLayout std140(MemoryLayout::Standard::k140);
4401*c8dee2aaSAndroid Build Coastguard Worker     SkSL::MemoryLayout native(nativeLayout);
4402*c8dee2aaSAndroid Build Coastguard Worker 
4403*c8dee2aaSAndroid Build Coastguard Worker     const Type& structType = interfaceBlock->var()->type().componentType();
4404*c8dee2aaSAndroid Build Coastguard Worker     for (const Field& field : structType.fields()) {
4405*c8dee2aaSAndroid Build Coastguard Worker         const Type* type = field.fType;
4406*c8dee2aaSAndroid Build Coastguard Worker         bool needsArrayPolyfill = false;
4407*c8dee2aaSAndroid Build Coastguard Worker         bool needsMatrixPolyfill = false;
4408*c8dee2aaSAndroid Build Coastguard Worker 
4409*c8dee2aaSAndroid Build Coastguard Worker         auto isPolyfillableMatrixType = [&](const Type* type) {
4410*c8dee2aaSAndroid Build Coastguard Worker             return type->isMatrix() && std140.stride(*type) != native.stride(*type);
4411*c8dee2aaSAndroid Build Coastguard Worker         };
4412*c8dee2aaSAndroid Build Coastguard Worker 
4413*c8dee2aaSAndroid Build Coastguard Worker         if (isPolyfillableMatrixType(type)) {
4414*c8dee2aaSAndroid Build Coastguard Worker             // Matrices will be represented as 16-byte aligned arrays in std140, and reconstituted
4415*c8dee2aaSAndroid Build Coastguard Worker             // into proper matrices as they are later accessed. We need to synthesize polyfill.
4416*c8dee2aaSAndroid Build Coastguard Worker             needsMatrixPolyfill = true;
4417*c8dee2aaSAndroid Build Coastguard Worker         } else if (type->isArray() && !type->isUnsizedArray() &&
4418*c8dee2aaSAndroid Build Coastguard Worker                    !type->componentType().isOpaque()) {
4419*c8dee2aaSAndroid Build Coastguard Worker             const Type* innerType = &type->componentType();
4420*c8dee2aaSAndroid Build Coastguard Worker             if (isPolyfillableMatrixType(innerType)) {
4421*c8dee2aaSAndroid Build Coastguard Worker                 // Use a polyfill when the array contains a matrix that requires polyfill.
4422*c8dee2aaSAndroid Build Coastguard Worker                 needsArrayPolyfill = true;
4423*c8dee2aaSAndroid Build Coastguard Worker                 needsMatrixPolyfill = true;
4424*c8dee2aaSAndroid Build Coastguard Worker             } else if (native.size(*innerType) < 16) {
4425*c8dee2aaSAndroid Build Coastguard Worker                 // Use a polyfill when the array elements are smaller than 16 bytes, since std140
4426*c8dee2aaSAndroid Build Coastguard Worker                 // will pad elements to a 16-byte stride.
4427*c8dee2aaSAndroid Build Coastguard Worker                 needsArrayPolyfill = true;
4428*c8dee2aaSAndroid Build Coastguard Worker             }
4429*c8dee2aaSAndroid Build Coastguard Worker         }
4430*c8dee2aaSAndroid Build Coastguard Worker 
4431*c8dee2aaSAndroid Build Coastguard Worker         if (needsArrayPolyfill || needsMatrixPolyfill) {
4432*c8dee2aaSAndroid Build Coastguard Worker             // Add a polyfill for this matrix type.
4433*c8dee2aaSAndroid Build Coastguard Worker             FieldPolyfillInfo info;
4434*c8dee2aaSAndroid Build Coastguard Worker             info.fInterfaceBlock = interfaceBlock;
4435*c8dee2aaSAndroid Build Coastguard Worker             info.fReplacementName = "_skUnpacked_" + std::string(instanceName) + '_' +
4436*c8dee2aaSAndroid Build Coastguard Worker                                     this->assembleName(field.fName);
4437*c8dee2aaSAndroid Build Coastguard Worker             info.fIsArray = needsArrayPolyfill;
4438*c8dee2aaSAndroid Build Coastguard Worker             info.fIsMatrix = needsMatrixPolyfill;
4439*c8dee2aaSAndroid Build Coastguard Worker             fFieldPolyfillMap.set(&field, info);
4440*c8dee2aaSAndroid Build Coastguard Worker         }
4441*c8dee2aaSAndroid Build Coastguard Worker     }
4442*c8dee2aaSAndroid Build Coastguard Worker }
4443*c8dee2aaSAndroid Build Coastguard Worker 
writeUniformsAndBuffers()4444*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeUniformsAndBuffers() {
4445*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : fProgram.elements()) {
4446*c8dee2aaSAndroid Build Coastguard Worker         // Iterate through the interface blocks.
4447*c8dee2aaSAndroid Build Coastguard Worker         if (!e->is<InterfaceBlock>()) {
4448*c8dee2aaSAndroid Build Coastguard Worker             continue;
4449*c8dee2aaSAndroid Build Coastguard Worker         }
4450*c8dee2aaSAndroid Build Coastguard Worker         const InterfaceBlock& ib = e->as<InterfaceBlock>();
4451*c8dee2aaSAndroid Build Coastguard Worker 
4452*c8dee2aaSAndroid Build Coastguard Worker         // Determine if this interface block holds uniforms, buffers, or something else (skip it).
4453*c8dee2aaSAndroid Build Coastguard Worker         std::string_view addressSpace;
4454*c8dee2aaSAndroid Build Coastguard Worker         std::string_view accessMode;
4455*c8dee2aaSAndroid Build Coastguard Worker         MemoryLayout::Standard nativeLayout;
4456*c8dee2aaSAndroid Build Coastguard Worker         if (ib.var()->modifierFlags().isUniform()) {
4457*c8dee2aaSAndroid Build Coastguard Worker             addressSpace = "uniform";
4458*c8dee2aaSAndroid Build Coastguard Worker             nativeLayout = MemoryLayout::Standard::kWGSLUniform_Base;
4459*c8dee2aaSAndroid Build Coastguard Worker         } else if (ib.var()->modifierFlags().isBuffer()) {
4460*c8dee2aaSAndroid Build Coastguard Worker             addressSpace = "storage";
4461*c8dee2aaSAndroid Build Coastguard Worker             nativeLayout = MemoryLayout::Standard::kWGSLStorage_Base;
4462*c8dee2aaSAndroid Build Coastguard Worker             accessMode = ib.var()->modifierFlags().isReadOnly() ? ", read" : ", read_write";
4463*c8dee2aaSAndroid Build Coastguard Worker         } else {
4464*c8dee2aaSAndroid Build Coastguard Worker             continue;
4465*c8dee2aaSAndroid Build Coastguard Worker         }
4466*c8dee2aaSAndroid Build Coastguard Worker 
4467*c8dee2aaSAndroid Build Coastguard Worker         // If we have an anonymous interface block, assign a name like `_uniform0` or `_storage1`.
4468*c8dee2aaSAndroid Build Coastguard Worker         std::string instanceName;
4469*c8dee2aaSAndroid Build Coastguard Worker         if (ib.instanceName().empty()) {
4470*c8dee2aaSAndroid Build Coastguard Worker             instanceName = "_" + std::string(addressSpace) + std::to_string(fScratchCount++);
4471*c8dee2aaSAndroid Build Coastguard Worker             fInterfaceBlockNameMap[&ib.var()->type().componentType()] = instanceName;
4472*c8dee2aaSAndroid Build Coastguard Worker         } else {
4473*c8dee2aaSAndroid Build Coastguard Worker             instanceName = std::string(ib.instanceName());
4474*c8dee2aaSAndroid Build Coastguard Worker         }
4475*c8dee2aaSAndroid Build Coastguard Worker 
4476*c8dee2aaSAndroid Build Coastguard Worker         this->prepareUniformPolyfillsForInterfaceBlock(&ib, instanceName, nativeLayout);
4477*c8dee2aaSAndroid Build Coastguard Worker 
4478*c8dee2aaSAndroid Build Coastguard Worker         // Create a struct to hold all of the fields from this InterfaceBlock.
4479*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!ib.typeName().empty());
4480*c8dee2aaSAndroid Build Coastguard Worker         this->write("struct ");
4481*c8dee2aaSAndroid Build Coastguard Worker         this->write(ib.typeName());
4482*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(" {");
4483*c8dee2aaSAndroid Build Coastguard Worker 
4484*c8dee2aaSAndroid Build Coastguard Worker         // Find the struct type and fields used by this interface block.
4485*c8dee2aaSAndroid Build Coastguard Worker         const Type& ibType = ib.var()->type().componentType();
4486*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(ibType.isStruct());
4487*c8dee2aaSAndroid Build Coastguard Worker 
4488*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const Field> ibFields = ibType.fields();
4489*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!ibFields.empty());
4490*c8dee2aaSAndroid Build Coastguard Worker 
4491*c8dee2aaSAndroid Build Coastguard Worker         MemoryLayout layout(MemoryLayout::Standard::k140);
4492*c8dee2aaSAndroid Build Coastguard Worker         this->writeFields(ibFields, &layout);
4493*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("};");
4494*c8dee2aaSAndroid Build Coastguard Worker         this->write("@group(");
4495*c8dee2aaSAndroid Build Coastguard Worker         this->write(std::to_string(std::max(0, ib.var()->layout().fSet)));
4496*c8dee2aaSAndroid Build Coastguard Worker         this->write(") @binding(");
4497*c8dee2aaSAndroid Build Coastguard Worker         this->write(std::to_string(std::max(0, ib.var()->layout().fBinding)));
4498*c8dee2aaSAndroid Build Coastguard Worker         this->write(") var<");
4499*c8dee2aaSAndroid Build Coastguard Worker         this->write(addressSpace);
4500*c8dee2aaSAndroid Build Coastguard Worker         this->write(accessMode);
4501*c8dee2aaSAndroid Build Coastguard Worker         this->write("> ");
4502*c8dee2aaSAndroid Build Coastguard Worker         this->write(instanceName);
4503*c8dee2aaSAndroid Build Coastguard Worker         this->write(" : ");
4504*c8dee2aaSAndroid Build Coastguard Worker         this->write(to_wgsl_type(fContext, ib.var()->type(), &ib.var()->layout()));
4505*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(";");
4506*c8dee2aaSAndroid Build Coastguard Worker     }
4507*c8dee2aaSAndroid Build Coastguard Worker }
4508*c8dee2aaSAndroid Build Coastguard Worker 
writeNonBlockUniformsForTests()4509*c8dee2aaSAndroid Build Coastguard Worker void WGSLCodeGenerator::writeNonBlockUniformsForTests() {
4510*c8dee2aaSAndroid Build Coastguard Worker     bool declaredUniformsStruct = false;
4511*c8dee2aaSAndroid Build Coastguard Worker 
4512*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : fProgram.elements()) {
4513*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<GlobalVarDeclaration>()) {
4514*c8dee2aaSAndroid Build Coastguard Worker             const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
4515*c8dee2aaSAndroid Build Coastguard Worker             const Variable& var = *decls.varDeclaration().var();
4516*c8dee2aaSAndroid Build Coastguard Worker             if (is_in_global_uniforms(var)) {
4517*c8dee2aaSAndroid Build Coastguard Worker                 if (!declaredUniformsStruct) {
4518*c8dee2aaSAndroid Build Coastguard Worker                     this->write("struct _GlobalUniforms {\n");
4519*c8dee2aaSAndroid Build Coastguard Worker                     declaredUniformsStruct = true;
4520*c8dee2aaSAndroid Build Coastguard Worker                 }
4521*c8dee2aaSAndroid Build Coastguard Worker                 this->write("  ");
4522*c8dee2aaSAndroid Build Coastguard Worker                 this->writeVariableDecl(var.layout(), var.type(), var.mangledName(),
4523*c8dee2aaSAndroid Build Coastguard Worker                                         Delimiter::kComma);
4524*c8dee2aaSAndroid Build Coastguard Worker             }
4525*c8dee2aaSAndroid Build Coastguard Worker         }
4526*c8dee2aaSAndroid Build Coastguard Worker     }
4527*c8dee2aaSAndroid Build Coastguard Worker     if (declaredUniformsStruct) {
4528*c8dee2aaSAndroid Build Coastguard Worker         int binding = fProgram.fConfig->fSettings.fDefaultUniformBinding;
4529*c8dee2aaSAndroid Build Coastguard Worker         int set = fProgram.fConfig->fSettings.fDefaultUniformSet;
4530*c8dee2aaSAndroid Build Coastguard Worker         this->write("};\n");
4531*c8dee2aaSAndroid Build Coastguard Worker         this->write("@binding(" + std::to_string(binding) + ") ");
4532*c8dee2aaSAndroid Build Coastguard Worker         this->write("@group(" + std::to_string(set) + ") ");
4533*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine("var<uniform> _globalUniforms: _GlobalUniforms;");
4534*c8dee2aaSAndroid Build Coastguard Worker     }
4535*c8dee2aaSAndroid Build Coastguard Worker }
4536*c8dee2aaSAndroid Build Coastguard Worker 
functionDependencyArgs(const FunctionDeclaration & f)4537*c8dee2aaSAndroid Build Coastguard Worker std::string WGSLCodeGenerator::functionDependencyArgs(const FunctionDeclaration& f) {
4538*c8dee2aaSAndroid Build Coastguard Worker     WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&f);
4539*c8dee2aaSAndroid Build Coastguard Worker     std::string args;
4540*c8dee2aaSAndroid Build Coastguard Worker     if (deps && *deps) {
4541*c8dee2aaSAndroid Build Coastguard Worker         const char* separator = "";
4542*c8dee2aaSAndroid Build Coastguard Worker         if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4543*c8dee2aaSAndroid Build Coastguard Worker             args += "_stageIn";
4544*c8dee2aaSAndroid Build Coastguard Worker             separator = ", ";
4545*c8dee2aaSAndroid Build Coastguard Worker         }
4546*c8dee2aaSAndroid Build Coastguard Worker         if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4547*c8dee2aaSAndroid Build Coastguard Worker             args += separator;
4548*c8dee2aaSAndroid Build Coastguard Worker             args += "_stageOut";
4549*c8dee2aaSAndroid Build Coastguard Worker         }
4550*c8dee2aaSAndroid Build Coastguard Worker     }
4551*c8dee2aaSAndroid Build Coastguard Worker     return args;
4552*c8dee2aaSAndroid Build Coastguard Worker }
4553*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionDependencyParams(const FunctionDeclaration & f)4554*c8dee2aaSAndroid Build Coastguard Worker bool WGSLCodeGenerator::writeFunctionDependencyParams(const FunctionDeclaration& f) {
4555*c8dee2aaSAndroid Build Coastguard Worker     WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&f);
4556*c8dee2aaSAndroid Build Coastguard Worker     if (!deps || !*deps) {
4557*c8dee2aaSAndroid Build Coastguard Worker         return false;
4558*c8dee2aaSAndroid Build Coastguard Worker     }
4559*c8dee2aaSAndroid Build Coastguard Worker 
4560*c8dee2aaSAndroid Build Coastguard Worker     std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
4561*c8dee2aaSAndroid Build Coastguard Worker     if (structNamePrefix.empty()) {
4562*c8dee2aaSAndroid Build Coastguard Worker         return false;
4563*c8dee2aaSAndroid Build Coastguard Worker     }
4564*c8dee2aaSAndroid Build Coastguard Worker     const char* separator = "";
4565*c8dee2aaSAndroid Build Coastguard Worker     if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4566*c8dee2aaSAndroid Build Coastguard Worker         this->write("_stageIn: ");
4567*c8dee2aaSAndroid Build Coastguard Worker         separator = ", ";
4568*c8dee2aaSAndroid Build Coastguard Worker         this->write(structNamePrefix);
4569*c8dee2aaSAndroid Build Coastguard Worker         this->write("In");
4570*c8dee2aaSAndroid Build Coastguard Worker     }
4571*c8dee2aaSAndroid Build Coastguard Worker     if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4572*c8dee2aaSAndroid Build Coastguard Worker         this->write(separator);
4573*c8dee2aaSAndroid Build Coastguard Worker         this->write("_stageOut: ptr<function, ");
4574*c8dee2aaSAndroid Build Coastguard Worker         this->write(structNamePrefix);
4575*c8dee2aaSAndroid Build Coastguard Worker         this->write("Out>");
4576*c8dee2aaSAndroid Build Coastguard Worker     }
4577*c8dee2aaSAndroid Build Coastguard Worker     return true;
4578*c8dee2aaSAndroid Build Coastguard Worker }
4579*c8dee2aaSAndroid Build Coastguard Worker 
ToWGSL(Program & program,const ShaderCaps * caps,OutputStream & out,PrettyPrint pp,IncludeSyntheticCode isc,ValidateWGSLProc validateWGSL)4580*c8dee2aaSAndroid Build Coastguard Worker bool ToWGSL(Program& program,
4581*c8dee2aaSAndroid Build Coastguard Worker             const ShaderCaps* caps,
4582*c8dee2aaSAndroid Build Coastguard Worker             OutputStream& out,
4583*c8dee2aaSAndroid Build Coastguard Worker             PrettyPrint pp,
4584*c8dee2aaSAndroid Build Coastguard Worker             IncludeSyntheticCode isc,
4585*c8dee2aaSAndroid Build Coastguard Worker             ValidateWGSLProc validateWGSL) {
4586*c8dee2aaSAndroid Build Coastguard Worker     TRACE_EVENT0("skia.shaders", "SkSL::ToWGSL");
4587*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(caps != nullptr);
4588*c8dee2aaSAndroid Build Coastguard Worker 
4589*c8dee2aaSAndroid Build Coastguard Worker     program.fContext->fErrors->setSource(*program.fSource);
4590*c8dee2aaSAndroid Build Coastguard Worker     bool result;
4591*c8dee2aaSAndroid Build Coastguard Worker     if (validateWGSL) {
4592*c8dee2aaSAndroid Build Coastguard Worker         StringStream wgsl;
4593*c8dee2aaSAndroid Build Coastguard Worker         WGSLCodeGenerator cg(program.fContext.get(), caps, &program, &wgsl, pp, isc);
4594*c8dee2aaSAndroid Build Coastguard Worker         result = cg.generateCode();
4595*c8dee2aaSAndroid Build Coastguard Worker         if (result) {
4596*c8dee2aaSAndroid Build Coastguard Worker             std::string_view wgslBytes = wgsl.str();
4597*c8dee2aaSAndroid Build Coastguard Worker             std::string warnings;
4598*c8dee2aaSAndroid Build Coastguard Worker             result = validateWGSL(*program.fContext->fErrors, wgslBytes, &warnings);
4599*c8dee2aaSAndroid Build Coastguard Worker             if (!warnings.empty()) {
4600*c8dee2aaSAndroid Build Coastguard Worker                 out.writeText("/* Tint reported warnings. */\n\n");
4601*c8dee2aaSAndroid Build Coastguard Worker             }
4602*c8dee2aaSAndroid Build Coastguard Worker             out.write(wgslBytes.data(), wgslBytes.size());
4603*c8dee2aaSAndroid Build Coastguard Worker         }
4604*c8dee2aaSAndroid Build Coastguard Worker     } else {
4605*c8dee2aaSAndroid Build Coastguard Worker         WGSLCodeGenerator cg(program.fContext.get(), caps, &program, &out, pp, isc);
4606*c8dee2aaSAndroid Build Coastguard Worker         result = cg.generateCode();
4607*c8dee2aaSAndroid Build Coastguard Worker     }
4608*c8dee2aaSAndroid Build Coastguard Worker     program.fContext->fErrors->setSource(std::string_view());
4609*c8dee2aaSAndroid Build Coastguard Worker 
4610*c8dee2aaSAndroid Build Coastguard Worker     return result;
4611*c8dee2aaSAndroid Build Coastguard Worker }
4612*c8dee2aaSAndroid Build Coastguard Worker 
ToWGSL(Program & program,const ShaderCaps * caps,OutputStream & out)4613*c8dee2aaSAndroid Build Coastguard Worker bool ToWGSL(Program& program, const ShaderCaps* caps, OutputStream& out) {
4614*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
4615*c8dee2aaSAndroid Build Coastguard Worker     constexpr PrettyPrint defaultPrintOpts = PrettyPrint::kYes;
4616*c8dee2aaSAndroid Build Coastguard Worker #else
4617*c8dee2aaSAndroid Build Coastguard Worker     constexpr PrettyPrint defaultPrintOpts = PrettyPrint::kNo;
4618*c8dee2aaSAndroid Build Coastguard Worker #endif
4619*c8dee2aaSAndroid Build Coastguard Worker     return ToWGSL(program, caps, out, defaultPrintOpts, IncludeSyntheticCode::kNo, nullptr);
4620*c8dee2aaSAndroid Build Coastguard Worker }
4621*c8dee2aaSAndroid Build Coastguard Worker 
ToWGSL(Program & program,const ShaderCaps * caps,std::string * out)4622*c8dee2aaSAndroid Build Coastguard Worker bool ToWGSL(Program& program, const ShaderCaps* caps, std::string* out) {
4623*c8dee2aaSAndroid Build Coastguard Worker     StringStream buffer;
4624*c8dee2aaSAndroid Build Coastguard Worker     if (!ToWGSL(program, caps, buffer)) {
4625*c8dee2aaSAndroid Build Coastguard Worker         return false;
4626*c8dee2aaSAndroid Build Coastguard Worker     }
4627*c8dee2aaSAndroid Build Coastguard Worker     *out = buffer.str();
4628*c8dee2aaSAndroid Build Coastguard Worker     return true;
4629*c8dee2aaSAndroid Build Coastguard Worker }
4630*c8dee2aaSAndroid Build Coastguard Worker 
4631*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
4632