Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / sksl / codegen / SkSLWGSLCodeGenerator.cpp
1 /*
2  * Copyright 2022 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7
8 #include "src/sksl/codegen/SkSLWGSLCodeGenerator.h"
9
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <vector>
14
15 #include "include/core/SkSpan.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/SkBitmaskEnum.h"
18 #include "include/private/SkSLLayout.h"
19 #include "include/private/SkSLModifiers.h"
20 #include "include/private/SkSLProgramElement.h"
21 #include "include/private/SkSLProgramKind.h"
22 #include "include/private/SkSLStatement.h"
23 #include "include/private/SkSLString.h"
24 #include "include/private/SkSLSymbol.h"
25 #include "include/private/SkTArray.h"
26 #include "include/sksl/SkSLErrorReporter.h"
27 #include "include/sksl/SkSLPosition.h"
28 #include "src/sksl/SkSLBuiltinTypes.h"
29 #include "src/sksl/SkSLCompiler.h"
30 #include "src/sksl/SkSLContext.h"
31 #include "src/sksl/SkSLOutputStream.h"
32 #include "src/sksl/SkSLProgramSettings.h"
33 #include "src/sksl/SkSLStringStream.h"
34 #include "src/sksl/SkSLUtil.h"
35 #include "src/sksl/analysis/SkSLProgramVisitor.h"
36 #include "src/sksl/ir/SkSLBinaryExpression.h"
37 #include "src/sksl/ir/SkSLBlock.h"
38 #include "src/sksl/ir/SkSLConstructor.h"
39 #include "src/sksl/ir/SkSLConstructorCompound.h"
40 #include "src/sksl/ir/SkSLExpression.h"
41 #include "src/sksl/ir/SkSLExpressionStatement.h"
42 #include "src/sksl/ir/SkSLFieldAccess.h"
43 #include "src/sksl/ir/SkSLFunctionCall.h"
44 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
45 #include "src/sksl/ir/SkSLFunctionDefinition.h"
46 #include "src/sksl/ir/SkSLInterfaceBlock.h"
47 #include "src/sksl/ir/SkSLLiteral.h"
48 #include "src/sksl/ir/SkSLProgram.h"
49 #include "src/sksl/ir/SkSLReturnStatement.h"
50 #include "src/sksl/ir/SkSLSwizzle.h"
51 #include "src/sksl/ir/SkSLSymbolTable.h"
52 #include "src/sksl/ir/SkSLType.h"
53 #include "src/sksl/ir/SkSLVarDeclarations.h"
54 #include "src/sksl/ir/SkSLVariable.h"
55 #include "src/sksl/ir/SkSLVariableReference.h"
56
57 // TODO(skia:13092): This is a temporary debug feature. Remove when the implementation is
58 // complete and this is no longer needed.
59 #define DUMP_SRC_IR 0
60
61 namespace SkSL {
62 namespace {
63
64 // See https://www.w3.org/TR/WGSL/#memory-view-types
65 enum class PtrAddressSpace {
66     kFunction,
67     kPrivate,
68     kStorage,
69 };
70
71 std::string_view pipeline_struct_prefix(ProgramKind kind) {
72     if (ProgramConfig::IsVertex(kind)) {
73         return "VS";
74     }
75     if (ProgramConfig::IsFragment(kind)) {
76         return "FS";
77     }
78     return "";
79 }
80
81 std::string_view address_space_to_str(PtrAddressSpace addressSpace) {
82     switch (addressSpace) {
83         case PtrAddressSpace::kFunction:
84             return "function";
85         case PtrAddressSpace::kPrivate:
86             return "private";
87         case PtrAddressSpace::kStorage:
88             return "storage";
89     }
90     SkDEBUGFAIL("unsupported ptr address space");
91     return "unsupported";
92 }
93
94 std::string_view to_scalar_type(const Type& type) {
95     SkASSERT(type.typeKind() == Type::TypeKind::kScalar);
96     switch (type.numberKind()) {
97         // Floating-point numbers in WebGPU currently always have 32-bit footprint and
98         // relaxed-precision is not supported without extensions. f32 is the only floating-point
99         // number type in WGSL (see the discussion on https://github.com/gpuweb/gpuweb/issues/658).
100         case Type::NumberKind::kFloat:
101             return "f32";
102         case Type::NumberKind::kSigned:
103             return "i32";
104         case Type::NumberKind::kUnsigned:
105             return "u32";
106         case Type::NumberKind::kBoolean:
107             return "bool";
108         case Type::NumberKind::kNonnumeric:
109             [[fallthrough]];
110         default:
111             break;
112     }
113     return type.name();
114 }
115
116 // Convert a SkSL type to a WGSL type. Handles all plain types except structure types
117 // (see https://www.w3.org/TR/WGSL/#plain-types-section).
118 std::string to_wgsl_type(const Type& type) {
119     // TODO(skia:13092): Handle array, matrix, sampler types.
120     switch (type.typeKind()) {
121         case Type::TypeKind::kScalar:
122             return std::string(to_scalar_type(type));
123         case Type::TypeKind::kVector:
124             return "vec" + std::to_string(type.columns()) + "<" +
125                    std::string(to_scalar_type(type.componentType())) + ">";
126         default:
127             break;
128     }
129     return std::string(type.name());
130 }
131
132 std::string to_ptr_type(const Type& type,
133                         PtrAddressSpace addressSpace = PtrAddressSpace::kFunction) {
134     return "ptr<" + std::string(address_space_to_str(addressSpace)) + ", " + to_wgsl_type(type) +
135            ">";
136 }
137
138 std::string_view wgsl_builtin_name(WGSLCodeGenerator::Builtin builtin) {
139     using Builtin = WGSLCodeGenerator::Builtin;
140     switch (builtin) {
141         case Builtin::kVertexIndex:
142             return "vertex_index";
143         case Builtin::kInstanceIndex:
144             return "instance_index";
145         case Builtin::kPosition:
146             return "position";
147         case Builtin::kFrontFacing:
148             return "front_facing";
149         case Builtin::kSampleIndex:
150             return "sample_index";
151         case Builtin::kFragDepth:
152             return "frag_depth";
153         case Builtin::kSampleMask:
154             return "sample_mask";
155         case Builtin::kLocalInvocationId:
156             return "local_invocation_id";
157         case Builtin::kLocalInvocationIndex:
158             return "local_invocation_index";
159         case Builtin::kGlobalInvocationId:
160             return "global_invocation_id";
161         case Builtin::kWorkgroupId:
162             return "workgroup_id";
163         case Builtin::kNumWorkgroups:
164             return "num_workgroups";
165         default:
166             break;
167     }
168
169     SkDEBUGFAIL("unsupported builtin");
170     return "unsupported";
171 }
172
173 std::string_view wgsl_builtin_type(WGSLCodeGenerator::Builtin builtin) {
174     using Builtin = WGSLCodeGenerator::Builtin;
175     switch (builtin) {
176         case Builtin::kVertexIndex:
177             return "u32";
178         case Builtin::kInstanceIndex:
179             return "u32";
180         case Builtin::kPosition:
181             return "vec4<f32>";
182         case Builtin::kFrontFacing:
183             return "bool";
184         case Builtin::kSampleIndex:
185             return "u32";
186         case Builtin::kFragDepth:
187             return "f32";
188         case Builtin::kSampleMask:
189             return "u32";
190         case Builtin::kLocalInvocationId:
191             return "vec3<u32>";
192         case Builtin::kLocalInvocationIndex:
193             return "u32";
194         case Builtin::kGlobalInvocationId:
195             return "vec3<u32>";
196         case Builtin::kWorkgroupId:
197             return "vec3<u32>";
198         case Builtin::kNumWorkgroups:
199             return "vec3<u32>";
200         default:
201             break;
202     }
203
204     SkDEBUGFAIL("unsupported builtin");
205     return "unsupported";
206 }
207
208 // Some built-in variables have a type that differs from their SkSL counterpart (e.g. signed vs
209 // unsigned integer). We handle these cases with an explicit type conversion during a variable
210 // reference. Returns the WGSL type of the conversion target if conversion is needed, otherwise
211 // returns std::nullopt.
212 std::optional<std::string_view> needs_builtin_type_conversion(const Variable& v) {
213     switch (v.modifiers().fLayout.fBuiltin) {
214         case SK_VERTEXID_BUILTIN:
215         case SK_INSTANCEID_BUILTIN:
216             return {"i32"};
217         default:
218             break;
219     }
220     return std::nullopt;
221 }
222
223 // Map a SkSL builtin flag to a WGSL builtin kind. Returns std::nullopt if `builtin` is not
224 // not supported for WGSL.
225 //
226 // Also see //src/sksl/sksl_vert.sksl and //src/sksl/sksl_frag.sksl for supported built-ins.
227 std::optional<WGSLCodeGenerator::Builtin> builtin_from_sksl_name(int builtin) {
228     using Builtin = WGSLCodeGenerator::Builtin;
229     switch (builtin) {
230         case SK_POSITION_BUILTIN:
231             [[fallthrough]];
232         case SK_FRAGCOORD_BUILTIN:
233             return {Builtin::kPosition};
234         case SK_VERTEXID_BUILTIN:
235             return {Builtin::kVertexIndex};
236         case SK_INSTANCEID_BUILTIN:
237             return {Builtin::kInstanceIndex};
238         case SK_CLOCKWISE_BUILTIN:
239             // TODO(skia:13092): While `front_facing` is the corresponding built-in, it does not
240             // imply a particular winding order. We correctly compute the face orientation based
241             // on how Skia configured the render pipeline for all references to this built-in
242             // variable (see `SkSL::Program::Inputs::fUseFlipRTUniform`).
243             return {Builtin::kFrontFacing};
244         default:
245             break;
246     }
247     return std::nullopt;
248 }
249
250 std::shared_ptr<SymbolTable> top_level_symbol_table(const FunctionDefinition& f) {
251     return f.body()->as<Block>().symbolTable()->fParent;
252 }
253
254 const char* delimiter_to_str(WGSLCodeGenerator::Delimiter delimiter) {
255     using Delim = WGSLCodeGenerator::Delimiter;
256     switch (delimiter) {
257         case Delim::kComma:
258             return ",";
259         case Delim::kSemicolon:
260             return ";";
261         case Delim::kNone:
262         default:
263             break;
264     }
265     return "";
266 }
267
268 // FunctionDependencyResolver visits the IR tree rooted at a particular function definition and
269 // computes that function's dependencies on pipeline stage IO parameters. These are later used to
270 // synthesize arguments when writing out function definitions.
271 class FunctionDependencyResolver : public ProgramVisitor {
272 public:
273     using Deps = WGSLCodeGenerator::FunctionDependencies;
274     using DepsMap = WGSLCodeGenerator::ProgramRequirements::DepsMap;
275
276     FunctionDependencyResolver(const Program* p,
277                                const FunctionDeclaration* f,
278                                DepsMap* programDependencyMap)
279             : fProgram(p), fFunction(f), fDependencyMap(programDependencyMap) {}
280
281     Deps resolve() {
282         fDeps = Deps::kNone;
283         this->visit(*fProgram);
284         return fDeps;
285     }
286
287 private:
288     bool visitProgramElement(const ProgramElement& p) override {
289         // Only visit the program that matches the requested function.
290         if (p.is<FunctionDefinition>() && &p.as<FunctionDefinition>().declaration() == fFunction) {
291             return INHERITED::visitProgramElement(p);
292         }
293         // Continue visiting other program elements.
294         return false;
295     }
296
297     bool visitExpression(const Expression& e) override {
298         if (e.is<VariableReference>()) {
299             const VariableReference& v = e.as<VariableReference>();
300             const Modifiers& modifiers = v.variable()->modifiers();
301             if (v.variable()->storage() == Variable::Storage::kGlobal) {
302                 if (modifiers.fFlags & Modifiers::kIn_Flag) {
303                     fDeps |= Deps::kPipelineInputs;
304                 }
305                 if (modifiers.fFlags & Modifiers::kOut_Flag) {
306                     fDeps |= Deps::kPipelineOutputs;
307                 }
308             }
309         } else if (e.is<FunctionCall>()) {
310             // The current function that we're processing (`fFunction`) inherits the dependencies of
311             // functions that it makes calls to, because the pipeline stage IO parameters need to be
312             // passed down as an argument.
313             const FunctionCall& callee = e.as<FunctionCall>();
314
315             // Don't process a function again if we have already resolved it.
316             Deps* found = fDependencyMap->find(&callee.function());
317             if (found) {
318                 fDeps |= *found;
319             } else {
320                 // Store the dependencies that have been discovered for the current function so far.
321                 // If `callee` directly or indirectly calls the current function, then this value
322                 // will prevent an infinite recursion.
323                 fDependencyMap->set(fFunction, fDeps);
324
325                 // Separately traverse the called function's definition and determine its
326                 // dependencies.
327                 FunctionDependencyResolver resolver(fProgram, &callee.function(), fDependencyMap);
328                 Deps calleeDeps = resolver.resolve();
329
330                 // Store the callee's dependencies in the global map to avoid processing
331                 // the function again for future calls.
332                 fDependencyMap->set(&callee.function(), calleeDeps);
333
334                 // Add to the current function's dependencies.
335                 fDeps |= calleeDeps;
336             }
337         }
338         return INHERITED::visitExpression(e);
339     }
340
341     const Program* const fProgram;
342     const FunctionDeclaration* const fFunction;
343     DepsMap* const fDependencyMap;
344     Deps fDeps = Deps::kNone;
345
346     using INHERITED = ProgramVisitor;
347 };
348
349 WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(const Program* program) {
350     bool mainNeedsCoordsArgument = false;
351     WGSLCodeGenerator::ProgramRequirements::DepsMap dependencies;
352
353     for (const ProgramElement* e : program->elements()) {
354         if (!e->is<FunctionDefinition>()) {
355             continue;
356         }
357
358         const FunctionDeclaration& decl = e->as<FunctionDefinition>().declaration();
359         if (decl.isMain()) {
360             for (const Variable* v : decl.parameters()) {
361                 if (v->modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN) {
362                     mainNeedsCoordsArgument = true;
363                     break;
364                 }
365             }
366         }
367
368         FunctionDependencyResolver resolver(program, &decl, &dependencies);
369         dependencies.set(&decl, resolver.resolve());
370     }
371
372     return WGSLCodeGenerator::ProgramRequirements(std::move(dependencies), mainNeedsCoordsArgument);
373 }
374
375 int count_pipeline_inputs(const Program* program) {
376     int inputCount = 0;
377     for (const ProgramElement* e : program->elements()) {
378         if (e->is<GlobalVarDeclaration>()) {
379             const Variable& v =
380                     e->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>().var();
381             if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
382                 inputCount++;
383             }
384         } else if (e->is<InterfaceBlock>()) {
385             const Variable& v = e->as<InterfaceBlock>().variable();
386             if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
387                 inputCount++;
388             }
389         }
390     }
391     return inputCount;
392 }
393
394 }  // namespace
395
396 bool WGSLCodeGenerator::generateCode() {
397     // The resources of a WGSL program are structured in the following way:
398     // - Vertex and fragment stage attribute inputs and outputs are bundled
399     //   inside synthetic structs called VSIn/VSOut/FSIn/FSOut.
400     // - All uniform and storage type resources are declared in global scope.
401     this->preprocessProgram();
402
403     StringStream header;
404     {
405         AutoOutputStream outputToHeader(this, &header, &fIndentation);
406         // TODO(skia:13092): Implement the following:
407         // - struct definitions
408         // - global uniform/storage resource declarations, including interface blocks.
409         this->writeStageInputStruct();
410         this->writeStageOutputStruct();
411     }
412     StringStream body;
413     {
414         AutoOutputStream outputToBody(this, &body, &fIndentation);
415         for (const ProgramElement* e : fProgram.elements()) {
416             this->writeProgramElement(*e);
417         }
418
419 // TODO(skia:13092): This is a temporary debug feature. Remove when the implementation is
420 // complete and this is no longer needed.
421 #if DUMP_SRC_IR
422         this->writeLine("\n----------");
423         this->writeLine("Source IR:\n");
424         for (const ProgramElement* e : fProgram.elements()) {
425             this->writeLine(e->description().c_str());
426         }
427 #endif
428     }
429
430     write_stringstream(header, *fOut);
431     write_stringstream(body, *fOut);
432     fContext.fErrors->reportPendingErrors(Position());
433     return fContext.fErrors->errorCount() == 0;
434 }
435
436 void WGSLCodeGenerator::preprocessProgram() {
437     fRequirements = resolve_program_requirements(&fProgram);
438     fPipelineInputCount = count_pipeline_inputs(&fProgram);
439 }
440
441 void WGSLCodeGenerator::write(std::string_view s) {
442     if (s.empty()) {
443         return;
444     }
445     if (fAtLineStart) {
446         for (int i = 0; i < fIndentation; i++) {
447             fOut->writeText("    ");
448         }
449     }
450     fOut->writeText(std::string(s).c_str());
451     fAtLineStart = false;
452 }
453
454 void WGSLCodeGenerator::writeLine(std::string_view s) {
455     this->write(s);
456     fOut->writeText("\n");
457     fAtLineStart = true;
458 }
459
460 void WGSLCodeGenerator::finishLine() {
461     if (!fAtLineStart) {
462         this->writeLine();
463     }
464 }
465
466 void WGSLCodeGenerator::writeName(std::string_view name) {
467     // Add underscore before name to avoid conflict with reserved words.
468     if (fReservedWords.contains(name)) {
469         this->write("_");
470     }
471     this->write(name);
472 }
473
474 void WGSLCodeGenerator::writePipelineIODeclaration(Modifiers modifiers,
475                                                    const Type& type,
476                                                    std::string_view name,
477                                                    Delimiter delimiter) {
478     // In WGSL, an entry-point IO parameter is "one of either a built-in value or
479     // assigned a location". However, some SkSL declarations, specifically sk_FragColor, can
480     // contain both a location and a builtin modifier. In addition, WGSL doesn't have a built-in
481     // equivalent for sk_FragColor as it relies on the user-defined location for a render
482     // target.
483     //
484     // Instead of special-casing sk_FragColor, we just give higher precedence to a location
485     // modifier if a declaration happens to both have a location and it's a built-in.
486     //
487     // Also see:
488     // https://www.w3.org/TR/WGSL/#input-output-locations
489     // https://www.w3.org/TR/WGSL/#attribute-location
490     // https://www.w3.org/TR/WGSL/#builtin-inputs-outputs
491     int location = modifiers.fLayout.fLocation;
492     if (location >= 0) {
493         this->writeUserDefinedIODecl(type, name, location, delimiter);
494     } else if (modifiers.fLayout.fBuiltin >= 0) {
495         auto builtin = builtin_from_sksl_name(modifiers.fLayout.fBuiltin);
496         if (builtin.has_value()) {
497             this->writeBuiltinIODecl(type, name, *builtin, delimiter);
498         }
499     }
500 }
501
502 void WGSLCodeGenerator::writeUserDefinedIODecl(const Type& type,
503                                                std::string_view name,
504                                                int location,
505                                                Delimiter delimiter) {
506     this->write("@location(" + std::to_string(location) + ") ");
507
508     // "User-defined IO of scalar or vector integer type must always be specified as
509     // @interpolate(flat)" (see https://www.w3.org/TR/WGSL/#interpolation)
510     if (type.isInteger() || (type.isVector() && type.componentType().isInteger())) {
511         this->write("@interpolate(flat) ");
512     }
513
514     this->writeName(name);
515     this->write(": " + to_wgsl_type(type));
516     this->writeLine(delimiter_to_str(delimiter));
517 }
518
519 void WGSLCodeGenerator::writeBuiltinIODecl(const Type& type,
520                                            std::string_view name,
521                                            Builtin builtin,
522                                            Delimiter delimiter) {
523     this->write("@builtin(");
524     this->write(wgsl_builtin_name(builtin));
525     this->write(") ");
526
527     this->writeName(name);
528     this->write(": ");
529     this->write(wgsl_builtin_type(builtin));
530     this->writeLine(delimiter_to_str(delimiter));
531 }
532
533 void WGSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
534     this->writeFunctionDeclaration(f.declaration());
535     this->write(" ");
536     this->writeBlock(f.body()->as<Block>());
537
538     if (f.declaration().isMain()) {
539         // We just emitted the user-defined main function. Next, we generate a program entry point
540         // that calls the user-defined main.
541         this->writeEntryPoint(f);
542     }
543 }
544
545 void WGSLCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f) {
546     this->write("fn ");
547     this->write(f.mangledName());
548     this->write("(");
549     const char* separator = "";
550     FunctionDependencies* deps = fRequirements.dependencies.find(&f);
551     if (deps) {
552         std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
553         if (structNamePrefix.length() != 0) {
554             if ((*deps & FunctionDependencies::kPipelineInputs) != FunctionDependencies::kNone) {
555                 separator = ", ";
556                 this->write("_stageIn: ");
557                 this->write(structNamePrefix);
558                 this->write("In");
559             }
560             if ((*deps & FunctionDependencies::kPipelineOutputs) != FunctionDependencies::kNone) {
561                 this->write(separator);
562                 separator = ", ";
563                 this->write("_stageOut: ptr<function, ");
564                 this->write(structNamePrefix);
565                 this->write("Out>");
566             }
567         }
568     }
569     for (const Variable* param : f.parameters()) {
570         this->write(separator);
571         separator = ", ";
572         this->writeName(param->name());
573         this->write(": ");
574
575         // Declare an "out" function parameter as a pointer.
576         if (param->modifiers().fFlags & Modifiers::kOut_Flag) {
577             this->write(to_ptr_type(param->type()));
578         } else {
579             this->write(to_wgsl_type(param->type()));
580         }
581     }
582     this->write(")");
583     if (!f.returnType().isVoid()) {
584         this->write(" -> ");
585         this->write(to_wgsl_type(f.returnType()));
586     }
587 }
588
589 void WGSLCodeGenerator::writeEntryPoint(const FunctionDefinition& main) {
590     SkASSERT(main.declaration().isMain());
591
592     // The input and output parameters for a vertex/fragment stage entry point function have the
593     // FSIn/FSOut/VSIn/VSOut struct types that have been synthesized in generateCode(). An entry
594     // point always has the same signature and acts as a trampoline to the user-defined main
595     // function.
596     std::string outputType;
597     if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
598         this->write("@stage(vertex) fn vertexMain(");
599         if (fPipelineInputCount > 0) {
600             this->write("_stageIn: VSIn");
601         }
602         this->writeLine(") -> VSOut {");
603         outputType = "VSOut";
604     } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
605         this->write("@stage(fragment) fn fragmentMain(");
606         if (fPipelineInputCount > 0) {
607             this->write("_stageIn: FSIn");
608         }
609         this->writeLine(") -> FSOut {");
610         outputType = "FSOut";
611     } else {
612         fContext.fErrors->error(Position(), "program kind not supported");
613         return;
614     }
615
616     // Declare the stage output struct.
617     fIndentation++;
618     this->write("var _stageOut: ");
619     this->write(outputType);
620     this->writeLine(";");
621
622     // Generate assignment to sk_FragColor built-in if the user-defined main returns a color.
623     if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
624         auto symbolTable = top_level_symbol_table(main);
625         const Symbol* symbol = (*symbolTable)["sk_FragColor"];
626         SkASSERT(symbol);
627         if (main.declaration().returnType().matches(symbol->type())) {
628             this->write("_stageOut.sk_FragColor = ");
629         }
630     }
631
632     // Generate the function call to the user-defined main:
633     this->write(main.declaration().mangledName());
634     this->write("(");
635     const char* separator = "";
636     FunctionDependencies* deps = fRequirements.dependencies.find(&main.declaration());
637     if (deps) {
638         if ((*deps & FunctionDependencies::kPipelineInputs) != FunctionDependencies::kNone) {
639             separator = ", ";
640             this->write("_stageIn");
641         }
642         if ((*deps & FunctionDependencies::kPipelineOutputs) != FunctionDependencies::kNone) {
643             this->write(separator);
644             separator = ", ";
645             this->write("&_stageOut");
646         }
647     }
648     // TODO(armansito): Handle arbitrary parameters.
649     if (main.declaration().parameters().size() != 0) {
650         const Variable* v = main.declaration().parameters()[0];
651         const Type& type = v->type();
652         if (v->modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN) {
653             if (!type.matches(*fContext.fTypes.fFloat2)) {
654                 fContext.fErrors->error(
655                         main.fPosition,
656                         "main function has unsupported parameter: " + type.description());
657                 return;
658             }
659
660             this->write(separator);
661             separator = ", ";
662             this->write("_stageIn.sk_FragCoord.xy");
663         }
664     }
665     this->writeLine(");");
666     this->writeLine("return _stageOut;");
667
668     fIndentation--;
669     this->writeLine("}");
670 }
671
672 void WGSLCodeGenerator::writeStatement(const Statement& s) {
673     switch (s.kind()) {
674         case Statement::Kind::kBlock:
675             this->writeBlock(s.as<Block>());
676             break;
677         case Statement::Kind::kExpression:
678             this->writeExpressionStatement(s.as<ExpressionStatement>());
679             break;
680         case Statement::Kind::kReturn:
681             this->writeReturnStatement(s.as<ReturnStatement>());
682             break;
683         case Statement::Kind::kVarDeclaration:
684             this->writeVarDeclaration(s.as<VarDeclaration>());
685             break;
686         default:
687             SkDEBUGFAILF("unsupported statement (kind: %d) %s", s.kind(), s.description().c_str());
688             break;
689     }
690 }
691
692 void WGSLCodeGenerator::writeStatements(const StatementArray& statements) {
693     for (const auto& s : statements) {
694         if (!s->isEmpty()) {
695             this->writeStatement(*s);
696             this->finishLine();
697         }
698     }
699 }
700
701 void WGSLCodeGenerator::writeBlock(const Block& b) {
702     // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
703     // something here to make the code valid).
704     bool isScope = b.isScope() || b.isEmpty();
705     if (isScope) {
706         this->writeLine("{");
707         fIndentation++;
708     }
709     this->writeStatements(b.children());
710     if (isScope) {
711         fIndentation--;
712         this->writeLine("}");
713     }
714 }
715
716 void WGSLCodeGenerator::writeExpressionStatement(const ExpressionStatement& s) {
717     if (s.expression()->hasSideEffects()) {
718         this->writeExpression(*s.expression(), Precedence::kTopLevel);
719         this->write(";");
720     }
721 }
722
723 void WGSLCodeGenerator::writeReturnStatement(const ReturnStatement& s) {
724     this->write("return");
725     if (s.expression()) {
726         this->write(" ");
727         this->writeExpression(*s.expression(), Precedence::kTopLevel);
728     }
729     this->write(";");
730 }
731
732 void WGSLCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl) {
733     bool isConst = varDecl.var().modifiers().fFlags & Modifiers::kConst_Flag;
734     if (isConst) {
735         this->write("let ");
736     } else {
737         this->write("var ");
738     }
739     this->writeName(varDecl.var().name());
740     this->write(": ");
741     this->write(to_wgsl_type(varDecl.var().type()));
742
743     if (varDecl.value()) {
744         this->write(" = ");
745         this->writeExpression(*varDecl.value(), Precedence::kTopLevel);
746     } else if (isConst) {
747         SkDEBUGFAILF("A let-declared constant must specify a value");
748     }
749
750     this->write(";");
751 }
752
753 void WGSLCodeGenerator::writeExpression(const Expression& e, Precedence parentPrecedence) {
754     switch (e.kind()) {
755         case Expression::Kind::kBinary:
756             this->writeBinaryExpression(e.as<BinaryExpression>(), parentPrecedence);
757             break;
758         case Expression::Kind::kConstructorCompound:
759             this->writeConstructorCompound(e.as<ConstructorCompound>(), parentPrecedence);
760             break;
761         case Expression::Kind::kConstructorCompoundCast:
762         case Expression::Kind::kConstructorScalarCast:
763             this->writeAnyConstructor(e.asAnyConstructor(), parentPrecedence);
764             break;
765         case Expression::Kind::kFieldAccess:
766             this->writeFieldAccess(e.as<FieldAccess>());
767             break;
768         case Expression::Kind::kLiteral:
769             this->writeLiteral(e.as<Literal>());
770             break;
771         case Expression::Kind::kSwizzle:
772             this->writeSwizzle(e.as<Swizzle>());
773             break;
774         case Expression::Kind::kVariableReference:
775             this->writeVariableReference(e.as<VariableReference>());
776             break;
777         default:
778             SkDEBUGFAILF("unsupported expression (kind: %d) %s",
779                          static_cast<int>(e.kind()),
780                          e.description().c_str());
781             break;
782     }
783 }
784
785 void WGSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
786                                               Precedence parentPrecedence) {
787     const Expression& left = *b.left();
788     const Expression& right = *b.right();
789     Operator op = b.getOperator();
790     Precedence precedence = op.getBinaryPrecedence();
791     bool needParens = precedence >= parentPrecedence;
792
793     if (needParens) {
794         this->write("(");
795     }
796
797     // TODO(skia:13092): Correctly handle the case when lhs is a pointer.
798
799     this->writeExpression(left, precedence);
800     this->write(op.operatorName());
801     this->writeExpression(right, precedence);
802
803     if (needParens) {
804         this->write(")");
805     }
806 }
807
808 void WGSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
809     const Type::Field* field = &f.base()->type().fields()[f.fieldIndex()];
810     if (FieldAccess::OwnerKind::kDefault == f.ownerKind()) {
811         this->writeExpression(*f.base(), Precedence::kPostfix);
812         this->write(".");
813     } else {
814         // We are accessing a field in an anonymous interface block. If the field refers to a
815         // pipeline IO parameter, then we access it via the synthesized IO structs. We make an
816         // explicit exception for `sk_PointSize` which we declare as a placeholder variable in
817         // global scope as it is not supported by WebGPU as a pipeline IO parameter (see comments
818         // in `writeStageOutputStruct`).
819         const Variable& v = *f.base()->as<VariableReference>().variable();
820         if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
821             this->write("_stageIn.");
822         } else if (v.modifiers().fFlags & Modifiers::kOut_Flag &&
823                    field->fModifiers.fLayout.fBuiltin != SK_POINTSIZE_BUILTIN) {
824             this->write("(*_stageOut).");
825         } else {
826             // TODO(skia:13902): Reference the variable using the base name used for its
827             // uniform/storage block global declaration.
828         }
829     }
830     this->writeName(field->fName);
831 }
832
833 void WGSLCodeGenerator::writeLiteral(const Literal& l) {
834     const Type& type = l.type();
835     if (type.isFloat()) {
836         this->write(skstd::to_string(l.floatValue()));
837         return;
838     }
839     if (type.isBoolean()) {
840         this->write(l.boolValue() ? "true" : "false");
841         return;
842     }
843     SkASSERT(type.isInteger());
844     if (type.matches(*fContext.fTypes.fUInt)) {
845         this->write(std::to_string(l.intValue() & 0xffffffff));
846         this->write("u");
847     } else if (type.matches(*fContext.fTypes.fUShort)) {
848         this->write(std::to_string(l.intValue() & 0xffff));
849         this->write("u");
850     } else {
851         this->write(std::to_string(l.intValue()));
852     }
853 }
854
855 void WGSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
856     this->writeExpression(*swizzle.base(), Precedence::kPostfix);
857     this->write(".");
858     for (int c : swizzle.components()) {
859         SkASSERT(c >= 0 && c <= 3);
860         this->write(&("x\0y\0z\0w\0"[c * 2]));
861     }
862 }
863
864 void WGSLCodeGenerator::writeVariableReference(const VariableReference& r) {
865     // TODO(skia:13902): Correctly handle function parameters.
866     // TODO(skia:13902): Correctly handle RTflip for built-ins.
867     const Variable& v = *r.variable();
868
869     // Insert a conversion expression if this is a built-in variable whose type differs from the
870     // SkSL.
871     std::optional<std::string_view> conversion = needs_builtin_type_conversion(v);
872     if (conversion.has_value()) {
873         this->write(*conversion);
874         this->write("(");
875     }
876     if (v.storage() == Variable::Storage::kGlobal) {
877         if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
878             this->write("_stageIn.");
879         } else if (v.modifiers().fFlags & Modifiers::kOut_Flag) {
880             this->write("(*_stageOut).");
881         }
882     }
883
884     this->writeName(v.name());
885
886     if (conversion.has_value()) {
887         this->write(")");
888     }
889 }
890
891 void WGSLCodeGenerator::writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence) {
892     this->write(to_wgsl_type(c.type()));
893     this->write("(");
894     const char* separator = "";
895     for (const auto& e : c.argumentSpan()) {
896         this->write(separator);
897         separator = ", ";
898         this->writeExpression(*e, Precedence::kSequence);
899     }
900     this->write(")");
901 }
902
903 void WGSLCodeGenerator::writeConstructorCompound(const ConstructorCompound& c,
904                                                  Precedence parentPrecedence) {
905     // TODO(skia:13092): Support matrix constructors
906     if (c.type().isVector()) {
907         this->writeConstructorCompoundVector(c, parentPrecedence);
908     } else {
909         fContext.fErrors->error(c.fPosition, "unsupported compound constructor");
910     }
911 }
912
913 void WGSLCodeGenerator::writeConstructorCompoundVector(const ConstructorCompound& c,
914                                                        Precedence parentPrecedence) {
915     // TODO(skia:13092): WGSL supports constructing vectors from a mix of scalars and vectors but
916     // not matrices. SkSL supports vec4(mat2x2) which we need to handle here
917     // (see https://www.w3.org/TR/WGSL/#type-constructor-expr).
918     this->writeAnyConstructor(c, parentPrecedence);
919 }
920
921 void WGSLCodeGenerator::writeProgramElement(const ProgramElement& e) {
922     switch (e.kind()) {
923         case ProgramElement::Kind::kExtension:
924             // TODO(skia:13092): WGSL supports extensions via the "enable" directive
925             // (https://www.w3.org/TR/WGSL/#language-extensions). While we could easily emit this
926             // directive, we should first ensure that all possible SkSL extension names are
927             // converted to their appropriate WGSL extension. Currently there are no known supported
928             // WGSL extensions aside from the hypotheticals listed in the spec.
929             break;
930         case ProgramElement::Kind::kGlobalVar:
931             // All global declarations are handled explicitly as the "program header" in
932             // generateCode().
933             break;
934         case ProgramElement::Kind::kInterfaceBlock:
935             // All interface block declarations are handled explicitly as the "program header" in
936             // generateCode().
937             break;
938         case ProgramElement::Kind::kStructDefinition:
939             // All struct type declarations are handled explicitly as the "program header" in
940             // generateCode().
941             break;
942         case ProgramElement::Kind::kFunctionPrototype:
943             // A WGSL function declaration must contain its body and the function name is in scope
944             // for the entire program (see https://www.w3.org/TR/WGSL/#function-declaration and
945             // https://www.w3.org/TR/WGSL/#declaration-and-scope).
946             //
947             // As such, we don't emit function prototypes.
948             break;
949         case ProgramElement::Kind::kFunction:
950             this->writeFunction(e.as<FunctionDefinition>());
951             break;
952         default:
953             SkDEBUGFAILF("unsupported program element: %s\n", e.description().c_str());
954             break;
955     }
956 }
957
958 void WGSLCodeGenerator::writeStageInputStruct() {
959     std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
960     if (structNamePrefix.empty()) {
961         // There's no need to declare pipeline stage outputs.
962         return;
963     }
964
965     // It is illegal to declare a struct with no members.
966     if (fPipelineInputCount < 1) {
967         return;
968     }
969
970     this->write("struct ");
971     this->write(structNamePrefix);
972     this->writeLine("In {");
973     fIndentation++;
974
975     bool declaredFragCoordsBuiltin = false;
976     for (const ProgramElement* e : fProgram.elements()) {
977         if (e->is<GlobalVarDeclaration>()) {
978             const Variable& v =
979                     e->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>().var();
980             if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
981                 this->writePipelineIODeclaration(
982                         v.modifiers(), v.type(), v.name(), Delimiter::kComma);
983                 if (v.modifiers().fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
984                     declaredFragCoordsBuiltin = true;
985                 }
986             }
987         } else if (e->is<InterfaceBlock>()) {
988             const Variable& v = e->as<InterfaceBlock>().variable();
989             // Merge all the members of `in` interface blocks to the input struct, which are
990             // specified as either "builtin" or with a "layout(location=".
991             //
992             // TODO(armansito): Is it legal to have an interface block without a storage qualifier
993             // but with members that have individual storage qualifiers?
994             if (v.modifiers().fFlags & Modifiers::kIn_Flag) {
995                 for (const auto& f : v.type().fields()) {
996                     this->writePipelineIODeclaration(
997                             f.fModifiers, *f.fType, f.fName, Delimiter::kComma);
998                     if (f.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
999                         declaredFragCoordsBuiltin = true;
1000                     }
1001                 }
1002             }
1003         }
1004     }
1005
1006     if (ProgramConfig::IsFragment(fProgram.fConfig->fKind) &&
1007         fRequirements.mainNeedsCoordsArgument && !declaredFragCoordsBuiltin) {
1008         this->writeLine("@builtin(position) sk_FragCoord: vec4<f32>,");
1009     }
1010
1011     fIndentation--;
1012     this->writeLine("};");
1013 }
1014
1015 void WGSLCodeGenerator::writeStageOutputStruct() {
1016     std::string_view structNamePrefix = pipeline_struct_prefix(fProgram.fConfig->fKind);
1017     if (structNamePrefix.empty()) {
1018         // There's no need to declare pipeline stage outputs.
1019         return;
1020     }
1021
1022     this->write("struct ");
1023     this->write(structNamePrefix);
1024     this->writeLine("Out {");
1025     fIndentation++;
1026
1027     // TODO(skia:13092): Remember all variables that are added to the output struct here so they
1028     // can be referenced correctly when handling variable references.
1029     bool declaredPositionBuiltin = false;
1030     bool requiresPointSizeBuiltin = false;
1031     for (const ProgramElement* e : fProgram.elements()) {
1032         if (e->is<GlobalVarDeclaration>()) {
1033             const Variable& v =
1034                     e->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>().var();
1035             if (v.modifiers().fFlags & Modifiers::kOut_Flag) {
1036                 this->writePipelineIODeclaration(
1037                         v.modifiers(), v.type(), v.name(), Delimiter::kComma);
1038             }
1039         } else if (e->is<InterfaceBlock>()) {
1040             const Variable& v = e->as<InterfaceBlock>().variable();
1041             // Merge all the members of `out` interface blocks to the output struct, which are
1042             // specified as either "builtin" or with a "layout(location=".
1043             //
1044             // TODO(armansito): Is it legal to have an interface block without a storage qualifier
1045             // but with members that have individual storage qualifiers?
1046             if (v.modifiers().fFlags & Modifiers::kOut_Flag) {
1047                 for (const auto& f : v.type().fields()) {
1048                     this->writePipelineIODeclaration(
1049                             f.fModifiers, *f.fType, f.fName, Delimiter::kComma);
1050                     if (f.fModifiers.fLayout.fBuiltin == SK_POSITION_BUILTIN) {
1051                         declaredPositionBuiltin = true;
1052                     } else if (f.fModifiers.fLayout.fBuiltin == SK_POINTSIZE_BUILTIN) {
1053                         // sk_PointSize is explicitly not supported by `builtin_from_sksl_name` so
1054                         // writePipelineIODeclaration will never write it. We mark it here if the
1055                         // declaration is needed so we can synthesize it below.
1056                         requiresPointSizeBuiltin = true;
1057                     }
1058                 }
1059             }
1060         }
1061     }
1062
1063     // A vertex program must include the `position` builtin in its entry point return type.
1064     if (ProgramConfig::IsVertex(fProgram.fConfig->fKind) && !declaredPositionBuiltin) {
1065         this->writeLine("@builtin(position) sk_Position: vec4<f32>,");
1066     }
1067
1068     fIndentation--;
1069     this->writeLine("};");
1070
1071     // In WebGPU/WGSL, the vertex stage does not support a point-size output and the size
1072     // of a point primitive is always 1 pixel (see https://github.com/gpuweb/gpuweb/issues/332).
1073     //
1074     // There isn't anything we can do to emulate this correctly at this stage so we
1075     // synthesize a placeholder variable that has no effect. Programs should not rely on
1076     // sk_PointSize when using the Dawn backend.
1077     if (ProgramConfig::IsVertex(fProgram.fConfig->fKind) && requiresPointSizeBuiltin) {
1078         this->writeLine("/* unsupported */ var<private> sk_PointSize: f32;");
1079     }
1080 }
1081
1082 }  // namespace SkSL