• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "compiler/translator/wgsl/RewritePipelineVariables.h"
8 
9 #include <string>
10 #include <utility>
11 
12 #include "GLES2/gl2.h"
13 #include "GLSLANG/ShaderLang.h"
14 #include "GLSLANG/ShaderVars.h"
15 #include "anglebase/no_destructor.h"
16 #include "common/angleutils.h"
17 #include "common/log_utils.h"
18 #include "compiler/translator/Common.h"
19 #include "compiler/translator/ImmutableString.h"
20 #include "compiler/translator/ImmutableStringBuilder.h"
21 #include "compiler/translator/IntermNode.h"
22 #include "compiler/translator/OutputTree.h"
23 #include "compiler/translator/Symbol.h"
24 #include "compiler/translator/SymbolUniqueId.h"
25 #include "compiler/translator/Types.h"
26 #include "compiler/translator/tree_util/BuiltIn_autogen.h"
27 #include "compiler/translator/tree_util/FindMain.h"
28 #include "compiler/translator/tree_util/ReplaceVariable.h"
29 #include "compiler/translator/util.h"
30 #include "compiler/translator/wgsl/Utils.h"
31 
32 namespace sh
33 {
34 
35 namespace
36 {
37 
38 const bool kOutputVariableUses = false;
39 
40 struct LocationAnnotation
41 {
42     // Most variables will not be assigned a location until link time, but some variables (like
43     // gl_FragColor) imply an output location.
44     int location = -1;
45 };
46 struct BuiltinAnnotation
47 {
48     ImmutableString wgslBuiltinName;
49 };
50 struct NoAnnotation
51 {};
52 
53 using PipelineAnnotation = std::variant<LocationAnnotation, BuiltinAnnotation, NoAnnotation>;
54 
55 enum class IOType
56 {
57     Input,
58     Output
59 };
60 
61 struct GlslToWgslBuiltinMapping
62 {
63     ImmutableString glslBuiltinName{nullptr};
64     PipelineAnnotation wgslPipelineAnnotation;
65     IOType ioType;
66     const TVariable *builtinVar;
67     // The type from the WGSL spec that corresponds to `wgslPipelineAnnotation`.
68     ImmutableString wgslBuiltinType{nullptr};
69     // The type that is expected by the shader in the AST, i.e. the type of `builtinVar`. If
70     // nullptr, is the same as `wgslBuiltinType`.
71     // TODO(anglebug.com/42267100): delete this and convert `builtinVar`'s type to a WGSL type.
72     ImmutableString wgslTypeExpectedByShader{nullptr};
73     // A function to apply that does one of two thing:
74     //   1. for an input builtin: converts the builtin, as supplied by WGPU, into the variable that
75     //   the GLSL shader expects.
76     //   2. for an output builtin: converts the output variable from the GLSL shader into the
77     //   builtin supplied back to WGPU.
78     // Can be nullptr for no conversion.
79     ImmutableString conversionFunc{nullptr};
80 };
81 
GetWgslBuiltinName(std::string glslBuiltinName,GLenum shaderType,GlslToWgslBuiltinMapping * outMapping)82 bool GetWgslBuiltinName(std::string glslBuiltinName,
83                         GLenum shaderType,
84                         GlslToWgslBuiltinMapping *outMapping)
85 {
86     static const angle::base::NoDestructor<angle::HashMap<std::string, GlslToWgslBuiltinMapping>>
87         kGlslBuiltinToWgslBuiltinVertex(
88             {{"gl_VertexID",
89               GlslToWgslBuiltinMapping{ImmutableString("gl_VertexID"),
90                                        BuiltinAnnotation{ImmutableString("vertex_index")},
91                                        IOType::Input, BuiltInVariable::gl_VertexID(),
92                                        ImmutableString("u32"), ImmutableString("i32"),
93                                        ImmutableString("i32")}},
94              {"gl_InstanceID",
95               GlslToWgslBuiltinMapping{ImmutableString("gl_InstanceID"),
96                                        BuiltinAnnotation{ImmutableString("instance_index")},
97                                        IOType::Input, BuiltInVariable::gl_InstanceID(),
98                                        ImmutableString("u32"), ImmutableString("i32"),
99                                        ImmutableString("i32")}},
100              {"gl_Position",
101               GlslToWgslBuiltinMapping{
102                   ImmutableString("gl_Position"), BuiltinAnnotation{ImmutableString("position")},
103                   IOType::Output, BuiltInVariable::gl_Position(), ImmutableString("vec4<f32>"),
104                   ImmutableString(nullptr), ImmutableString(nullptr)}},
105              {"gl_PointSize",
106               GlslToWgslBuiltinMapping{ImmutableString("gl_PointSize"), NoAnnotation{},
107                                        IOType::Output, BuiltInVariable::gl_PointSize(),
108                                        ImmutableString("f32"), ImmutableString(nullptr),
109                                        ImmutableString(nullptr)}},
110              // TODO(anglebug.com/42267100): might have to emulate clip_distances, see
111              // Metal's
112              // https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/msl/TranslatorMSL.cpp?q=symbol%3A%5Cbsh%3A%3AEmulateClipDistanceVaryings%5Cb%20case%3Ayes
113              {"gl_ClipDistance",
114               GlslToWgslBuiltinMapping{ImmutableString("gl_ClipDistance"),
115                                        BuiltinAnnotation{ImmutableString("clip_distances")},
116                                        IOType::Output, nullptr, ImmutableString("TODO"),
117                                        ImmutableString(nullptr), ImmutableString(nullptr)}}});
118     static const angle::base::NoDestructor<angle::HashMap<std::string, GlslToWgslBuiltinMapping>>
119         kGlslBuiltinToWgslBuiltinFragment({
120             {"gl_FragCoord",
121              GlslToWgslBuiltinMapping{ImmutableString("gl_FragCoord"),
122                                       BuiltinAnnotation{ImmutableString("position")}, IOType::Input,
123                                       BuiltInVariable::gl_FragCoord(), ImmutableString("vec4<f32>"),
124                                       ImmutableString(nullptr), ImmutableString(nullptr)}},
125             {"gl_FrontFacing",
126              GlslToWgslBuiltinMapping{ImmutableString("gl_FrontFacing"),
127                                       BuiltinAnnotation{ImmutableString("front_facing")},
128                                       IOType::Input, BuiltInVariable::gl_FrontFacing(),
129                                       ImmutableString("bool"), ImmutableString(nullptr),
130                                       ImmutableString(nullptr)}},
131             {"gl_SampleID",
132              GlslToWgslBuiltinMapping{
133                  ImmutableString("gl_SampleID"), BuiltinAnnotation{ImmutableString("sample_index")},
134                  IOType::Input, BuiltInVariable::gl_SampleID(), ImmutableString("u32"),
135                  ImmutableString("i32"), ImmutableString("i32")}},
136             // TODO(anglebug.com/42267100): gl_SampleMask is GLSL 4.00 or ARB_sample_shading and
137             // requires some special handling (see Metal).
138             {"gl_SampleMaskIn",
139              GlslToWgslBuiltinMapping{ImmutableString("gl_SampleMaskIn"),
140                                       BuiltinAnnotation{ImmutableString("sample_mask")},
141                                       IOType::Input, nullptr, ImmutableString("u32"),
142                                       ImmutableString("i32"), ImmutableString("i32")}},
143             // Just translate FragColor into a location = 0 out variable.
144             // TODO(anglebug.com/42267100): maybe ASSERT that there are no user-defined output
145             // variables? Is it possible for there to be other output variables when using
146             // FragColor?
147             {"gl_FragColor",
148              GlslToWgslBuiltinMapping{ImmutableString("gl_FragColor"), LocationAnnotation{0},
149                                       IOType::Output, BuiltInVariable::gl_FragColor(),
150                                       ImmutableString("vec4<f32>"), ImmutableString(nullptr),
151                                       ImmutableString(nullptr)}},
152             {"gl_SampleMask",
153              GlslToWgslBuiltinMapping{ImmutableString("gl_SampleMask"),
154                                       BuiltinAnnotation{ImmutableString("sample_mask")},
155                                       IOType::Output, nullptr, ImmutableString("u32"),
156                                       ImmutableString("i32"), ImmutableString("i32")}},
157             {"gl_FragDepth",
158              GlslToWgslBuiltinMapping{
159                  ImmutableString("gl_FragDepth"), BuiltinAnnotation{ImmutableString("frag_depth")},
160                  IOType::Output, BuiltInVariable::gl_FragDepth(), ImmutableString("f32"),
161                  ImmutableString(nullptr), ImmutableString(nullptr)}},
162         });
163     // TODO(anglebug.com/42267100): gl_FragData needs to be emulated. Need something
164     // like spir-v's
165     // third_party/angle/src/compiler/translator/tree_ops/spirv/EmulateFragColorData.h.
166 
167     if (shaderType == GL_VERTEX_SHADER)
168     {
169         auto it = kGlslBuiltinToWgslBuiltinVertex->find(glslBuiltinName);
170         if (it == kGlslBuiltinToWgslBuiltinVertex->end())
171         {
172             return false;
173         }
174         *outMapping = it->second;
175         return true;
176     }
177     else if (shaderType == GL_FRAGMENT_SHADER)
178     {
179         auto it = kGlslBuiltinToWgslBuiltinFragment->find(glslBuiltinName);
180         if (it == kGlslBuiltinToWgslBuiltinFragment->end())
181         {
182             return false;
183         }
184         *outMapping = it->second;
185         return true;
186     }
187     else
188     {
189         UNREACHABLE();
190         return false;
191     }
192 }
193 
CreateNameToReplaceBuiltin(ImmutableString glslBuiltinName)194 ImmutableString CreateNameToReplaceBuiltin(ImmutableString glslBuiltinName)
195 {
196     ImmutableStringBuilder newName(glslBuiltinName.length() + 1);
197     newName << glslBuiltinName << '_';
198     return newName;
199 }
200 
201 }  // namespace
202 
203 // Friended by RewritePipelineVarOutput
204 class RewritePipelineVarOutputBuilder
205 {
206   public:
207     static bool GenerateMainFunctionAndIOStructs(TCompiler &compiler,
208                                                  TIntermBlock &root,
209                                                  RewritePipelineVarOutput &outVarReplacements);
210 
211   private:
212     static bool GeneratePipelineStructStrings(
213         RewritePipelineVarOutput::WgslIOBlock *ioblock,
214         RewritePipelineVarOutput::RewrittenVarSet *varsToReplace,
215         ImmutableString toStruct,
216         ImmutableString fromStruct,
217         const std::vector<ShaderVariable> &shaderVars,
218         const GlobalVars &globalVars,
219         TCompiler &compiler,
220         IOType ioType,
221         const std::string &debugString);
222 
GenerateForBuiltinVar(RewritePipelineVarOutput::WgslIOBlock * ioblock,RewritePipelineVarOutput::RewrittenVarSet * varsToReplace,ImmutableString toStruct,ImmutableString fromStruct,TCompiler & compiler,IOType ioType,const std::string & shaderVarName)223     static bool GenerateForBuiltinVar(RewritePipelineVarOutput::WgslIOBlock *ioblock,
224                                       RewritePipelineVarOutput::RewrittenVarSet *varsToReplace,
225                                       ImmutableString toStruct,
226                                       ImmutableString fromStruct,
227                                       TCompiler &compiler,
228                                       IOType ioType,
229                                       const std::string &shaderVarName)
230     {
231 
232         GlslToWgslBuiltinMapping wgslName;
233         if (!GetWgslBuiltinName(shaderVarName, compiler.getShaderType(), &wgslName))
234         {
235             return false;
236         }
237 
238         const TVariable *varToReplace = wgslName.builtinVar;
239 
240         if (varToReplace == nullptr)
241         {
242             // Should be declared somewhere as a symbol.
243             // TODO(anglebug.com/42267100): Not sure if this ever actually occurs. Will this
244             // TVariable also have a declaration? Are there any gl_ variable that require or
245             // even allow declaration?
246             varToReplace = static_cast<const TVariable *>(compiler.getSymbolTable().findBuiltIn(
247                 ImmutableString(wgslName.glslBuiltinName), compiler.getShaderVersion()));
248             if (kOutputVariableUses)
249             {
250                 std::cout << "Var " << shaderVarName
251                           << " did not have a BuiltIn var but does have a builtin in the symbol "
252                              "table"
253                           << std::endl;
254             }
255         }
256 
257         ASSERT(ioType == wgslName.ioType);
258 
259         varsToReplace->insert(varToReplace->uniqueId().get());
260 
261         ImmutableString builtinReplacement = CreateNameToReplaceBuiltin(wgslName.glslBuiltinName);
262 
263         // E.g. `gl_VertexID_ : i32`.
264         ImmutableString globalType = wgslName.wgslTypeExpectedByShader.empty()
265                                          ? wgslName.wgslBuiltinType
266                                          : wgslName.wgslTypeExpectedByShader;
267         ImmutableString globalStructVar =
268             BuildConcatenatedImmutableString(builtinReplacement, " : ", globalType, ",");
269         ioblock->angleGlobalMembers.push_back(globalStructVar);
270 
271         if (auto *builtinAnnotation =
272                 std::get_if<BuiltinAnnotation>(&wgslName.wgslPipelineAnnotation))
273         {
274             // E.g. `@builtin(vertex_index) gl_VertexID_ : u32,`.
275             const char *builtinAnnotationStart = "@builtin(";
276             const char *builtinAnnotationEnd   = ") ";
277             ImmutableString annotatedStructVar = BuildConcatenatedImmutableString(
278                 builtinAnnotationStart, builtinAnnotation->wgslBuiltinName, builtinAnnotationEnd,
279                 builtinReplacement, " : ", wgslName.wgslBuiltinType, ",");
280             ioblock->angleAnnotatedMembers.push_back(annotatedStructVar);
281         }
282         else if (auto *locationAnnotation =
283                      std::get_if<LocationAnnotation>(&wgslName.wgslPipelineAnnotation))
284         {
285             ASSERT(locationAnnotation->location == 0);
286             // E.g. `@location(0) gl_FragColor_ : vec4<f32>,`.
287             const char *locationAnnotationStr  = "@location(0) ";
288             ImmutableString annotatedStructVar = BuildConcatenatedImmutableString(
289                 locationAnnotationStr, builtinReplacement, " : ", wgslName.wgslBuiltinType, ",");
290             ioblock->angleAnnotatedMembers.push_back(annotatedStructVar);
291         }
292         else
293         {
294             ASSERT(std::get_if<NoAnnotation>(&wgslName.wgslPipelineAnnotation));
295         }
296 
297         if (!std::get_if<NoAnnotation>(&wgslName.wgslPipelineAnnotation))
298         {
299             // E.g. `ANGLE_input_global.gl_VertexID_ = u32(ANGLE_input_annotated.gl_VertexID_);`
300             ImmutableString conversion(nullptr);
301             if (wgslName.conversionFunc.empty())
302             {
303                 conversion =
304                     BuildConcatenatedImmutableString(toStruct, ".", builtinReplacement, " = ",
305                                                      fromStruct, ".", builtinReplacement, ";");
306             }
307             else
308             {
309                 conversion = BuildConcatenatedImmutableString(
310                     toStruct, ".", builtinReplacement, " = ", wgslName.conversionFunc, "(",
311                     fromStruct, ".", builtinReplacement, ");");
312             }
313             ioblock->angleConversionFuncs.push_back(conversion);
314         }
315 
316         return true;
317     }
318 };
319 
320 // Given a list of `shaderVars` (as well as `compiler` and a list of global variables in the GLSL
321 // source, `globalVars`), computes the fields that should appear in the input/output pipeline
322 // structs and the annotations that should appear in the WGSL source.
323 //
324 // `ioblock` will be filled with strings that make up the resulting structs, and with the strings
325 // indicated by `fromStruct` and `toStruct`. `varsToReplace` will be filled with the symbols that
326 // should be replaced in the final WGSL source wtih struct accesses.
327 //
328 // Finally, `debugString` should describe `shaderVars` (e.g. "input varyings"), and `ioType`
329 // indicates whether `shaderVars` is meant to be an input or output variable, which is useful for
330 // debugging asserts.
GeneratePipelineStructStrings(RewritePipelineVarOutput::WgslIOBlock * ioblock,RewritePipelineVarOutput::RewrittenVarSet * varsToReplace,ImmutableString toStruct,ImmutableString fromStruct,const std::vector<ShaderVariable> & shaderVars,const GlobalVars & globalVars,TCompiler & compiler,IOType ioType,const std::string & debugString)331 [[nodiscard]] bool RewritePipelineVarOutputBuilder::GeneratePipelineStructStrings(
332     RewritePipelineVarOutput::WgslIOBlock *ioblock,
333     RewritePipelineVarOutput::RewrittenVarSet *varsToReplace,
334     ImmutableString toStruct,
335     ImmutableString fromStruct,
336     const std::vector<ShaderVariable> &shaderVars,
337     const GlobalVars &globalVars,
338     TCompiler &compiler,
339     IOType ioType,
340     const std::string &debugString)
341 {
342     for (const ShaderVariable &shaderVar : shaderVars)
343     {
344         if (shaderVar.name == "gl_FragData" || shaderVar.name == "gl_SecondaryFragColorEXT" ||
345             shaderVar.name == "gl_SecondaryFragDataEXT")
346         {
347             // TODO(anglebug.com/42267100): declare gl_FragData as multiple variables.
348             UNIMPLEMENTED();
349             return false;
350         }
351 
352         if (kOutputVariableUses)
353         {
354             std::cout << "Use of " << (shaderVar.isBuiltIn() ? "builtin " : "") << debugString
355                       << ": " << shaderVar.name << std::endl;
356         }
357 
358         if (shaderVar.isBuiltIn())
359         {
360             if (!GenerateForBuiltinVar(ioblock, varsToReplace, toStruct, fromStruct, compiler,
361                                        ioType, shaderVar.name))
362             {
363                 return false;
364             }
365         }
366         else
367         {
368             if (!shaderVar.active)
369             {
370                 // Skip any inactive attributes as they won't be assigned a location anyway.
371                 continue;
372             }
373 
374             TIntermDeclaration *declNode = globalVars.find(shaderVar.name)->second;
375             const TVariable *astVar      = &ViewDeclaration(*declNode).symbol.variable();
376 
377             const ImmutableString &userVarName = astVar->name();
378 
379             varsToReplace->insert(astVar->uniqueId().get());
380 
381             // E.g. `_uuserVar : i32,`.
382             TStringStream typeStream;
383             WriteWgslType(typeStream, astVar->getType(), {});
384             TString type = typeStream.str();
385             ImmutableString globalStructVar =
386                 BuildConcatenatedImmutableString(userVarName, " : ", type.c_str(), ",");
387             ioblock->angleGlobalMembers.push_back(globalStructVar);
388 
389             // E.g. `@location(@@@@@@) _uuserVar : i32,`.
390             const char *locationAnnotationStr = "@location(@@@@@@) ";
391             ImmutableString annotatedStructVar =
392                 BuildConcatenatedImmutableString(locationAnnotationStr, globalStructVar);
393             ioblock->angleAnnotatedMembers.push_back(annotatedStructVar);
394 
395             // E.g. `ANGLE_input_global._uuserVar = ANGLE_input_annotated._uuserVar;`
396             ImmutableString conversion = BuildConcatenatedImmutableString(
397                 toStruct, ".", userVarName, " = ", fromStruct, ".", userVarName, ";");
398             ioblock->angleConversionFuncs.push_back(conversion);
399         }
400     }
401 
402     return true;
403 }
404 
GenerateMainFunctionAndIOStructs(TCompiler & compiler,TIntermBlock & root,RewritePipelineVarOutput & outVarReplacements)405 bool RewritePipelineVarOutputBuilder::GenerateMainFunctionAndIOStructs(
406     TCompiler &compiler,
407     TIntermBlock &root,
408     RewritePipelineVarOutput &outVarReplacements)
409 {
410     GlobalVars globalVars = FindGlobalVars(&root);
411 
412     // The Dawn WGSL compiler generates an error if there is no builtin(position) variable in a
413     // vertex shader, though it doesn't look like the WGSL spec requires this. GLSL doesn't require
414     // use of gl_Position (only that its value is undefined if not written to). So, generate a
415     // @builtin(position) variable by pretending gl_Position is present even if it's not.
416     if (compiler.getShaderType() == GL_VERTEX_SHADER)
417     {
418         bool hasPosition = false;
419         for (const ShaderVariable &shaderVar : compiler.getOutputVaryings())
420         {
421             if (shaderVar.name == std::string("gl_Position"))
422             {
423                 hasPosition = true;
424             }
425         }
426 
427         if (!hasPosition)
428         {
429             if (!GenerateForBuiltinVar(
430                     &outVarReplacements.mOutputBlock, &outVarReplacements.mAngleOutputVars,
431                     /*toStruct=*/ImmutableString(kBuiltinOutputAnnotatedStructName),
432                     /*fromStruct=*/ImmutableString(kBuiltinOutputStructName), compiler,
433                     IOType::Output, "gl_Position"))
434             {
435                 return false;
436             }
437         }
438     }
439 
440     if (!RewritePipelineVarOutputBuilder::GeneratePipelineStructStrings(
441             &outVarReplacements.mInputBlock, &outVarReplacements.mAngleInputVars,
442             /*toStruct=*/ImmutableString(kBuiltinInputStructName),
443             /*fromStruct=*/ImmutableString(kBuiltinInputAnnotatedStructName),
444             compiler.getInputVaryings(), globalVars, compiler, IOType::Input, "input varyings") ||
445         !RewritePipelineVarOutputBuilder::GeneratePipelineStructStrings(
446             &outVarReplacements.mInputBlock, &outVarReplacements.mAngleInputVars,
447             /*toStruct=*/ImmutableString(kBuiltinInputStructName),
448             /*fromStruct=*/ImmutableString(kBuiltinInputAnnotatedStructName),
449             compiler.getAttributes(), globalVars, compiler, IOType::Input, "input attributes") ||
450         !RewritePipelineVarOutputBuilder::GeneratePipelineStructStrings(
451             &outVarReplacements.mOutputBlock, &outVarReplacements.mAngleOutputVars,
452             /*toStruct=*/ImmutableString(kBuiltinOutputAnnotatedStructName),
453             /*fromStruct=*/ImmutableString(kBuiltinOutputStructName), compiler.getOutputVaryings(),
454             globalVars, compiler, IOType::Output, "output varyings") ||
455         !RewritePipelineVarOutputBuilder::GeneratePipelineStructStrings(
456             &outVarReplacements.mOutputBlock, &outVarReplacements.mAngleOutputVars,
457             /*toStruct=*/ImmutableString(kBuiltinOutputAnnotatedStructName),
458             /*fromStruct=*/ImmutableString(kBuiltinOutputStructName), compiler.getOutputVariables(),
459             globalVars, compiler, IOType::Output, "output variables"))
460     {
461         return false;
462     }
463 
464     return true;
465 }
466 
RewritePipelineVarOutput(sh::GLenum shaderType)467 RewritePipelineVarOutput::RewritePipelineVarOutput(sh::GLenum shaderType) : mShaderType(shaderType)
468 {}
469 
IsInputVar(TSymbolUniqueId angleInputVar) const470 bool RewritePipelineVarOutput::IsInputVar(TSymbolUniqueId angleInputVar) const
471 {
472     return mAngleInputVars.count(angleInputVar.get()) > 0;
473 }
IsOutputVar(TSymbolUniqueId angleOutputVar) const474 bool RewritePipelineVarOutput::IsOutputVar(TSymbolUniqueId angleOutputVar) const
475 {
476     return mAngleOutputVars.count(angleOutputVar.get()) > 0;
477 }
478 
479 // static
OutputIOStruct(TInfoSinkBase & output,WgslIOBlock & block,ImmutableString builtinStructType,ImmutableString builtinStructName,ImmutableString builtinAnnotatedStructType)480 bool RewritePipelineVarOutput::OutputIOStruct(TInfoSinkBase &output,
481                                               WgslIOBlock &block,
482                                               ImmutableString builtinStructType,
483                                               ImmutableString builtinStructName,
484                                               ImmutableString builtinAnnotatedStructType)
485 {
486 
487     if (!block.angleGlobalMembers.empty())
488     {
489         // Output global struct definition.
490         output << "struct " << builtinStructType << " {\n";
491         for (const ImmutableString &globalMember : block.angleGlobalMembers)
492         {
493             output << "  " << globalMember << "\n";
494         }
495         output << "};\n\n";
496         // Output decl of global struct.
497         output << "var<private> " << builtinStructName << " : " << builtinStructType << ";\n\n";
498         // Output annotated struct definition.
499         output << "struct " << builtinAnnotatedStructType << " {\n";
500         for (const ImmutableString &annotatedMember : block.angleAnnotatedMembers)
501         {
502             output << "  " << annotatedMember << "\n";
503         }
504         output << "};\n\n";
505     }
506 
507     return true;
508 }
509 
OutputStructs(TInfoSinkBase & output)510 bool RewritePipelineVarOutput::OutputStructs(TInfoSinkBase &output)
511 {
512     if (!OutputIOStruct(output, mInputBlock, ImmutableString(kBuiltinInputStructType),
513                         ImmutableString(kBuiltinInputStructName),
514                         ImmutableString(kBuiltinInputAnnotatedStructType)) ||
515         !OutputIOStruct(output, mOutputBlock, ImmutableString(kBuiltinOutputStructType),
516                         ImmutableString(kBuiltinOutputStructName),
517                         ImmutableString(kBuiltinOutputAnnotatedStructType)))
518     {
519         return false;
520     }
521 
522     return true;
523 }
524 
525 // Could split OutputMainFunction() into the different parts of the main function.
OutputMainFunction(TInfoSinkBase & output)526 bool RewritePipelineVarOutput::OutputMainFunction(TInfoSinkBase &output)
527 {
528     if (mShaderType == GL_VERTEX_SHADER)
529     {
530         output << "@vertex\n";
531     }
532     else
533     {
534         ASSERT(mShaderType == GL_FRAGMENT_SHADER);
535         output << "@fragment\n";
536     }
537     output << "fn wgslMain(";
538     if (!mInputBlock.angleGlobalMembers.empty())
539     {
540         output << kBuiltinInputAnnotatedStructName << " : " << kBuiltinInputAnnotatedStructType;
541     }
542     output << ")";
543     if (!mOutputBlock.angleGlobalMembers.empty())
544     {
545         output << " -> " << kBuiltinOutputAnnotatedStructType;
546     }
547     output << "\n{\n";
548     for (const ImmutableString &conversionFunc : mInputBlock.angleConversionFuncs)
549     {
550         output << "  " << conversionFunc << "\n";
551     }
552     output << "  " << kUserDefinedNamePrefix << "main()" << ";\n";
553 
554     if (!mOutputBlock.angleGlobalMembers.empty())
555     {
556         output << "  var " << kBuiltinOutputAnnotatedStructName << " : "
557                << kBuiltinOutputAnnotatedStructType << ";\n";
558         for (const ImmutableString &conversionFunc : mOutputBlock.angleConversionFuncs)
559         {
560             output << "  " << conversionFunc << "\n";
561         }
562         output << "  return " << kBuiltinOutputAnnotatedStructName << ";\n";
563     }
564     output << "}\n";
565     return true;
566 }
567 
GenerateMainFunctionAndIOStructs(TCompiler & compiler,TIntermBlock & root,RewritePipelineVarOutput & outVarReplacements)568 bool GenerateMainFunctionAndIOStructs(TCompiler &compiler,
569                                       TIntermBlock &root,
570                                       RewritePipelineVarOutput &outVarReplacements)
571 {
572     return RewritePipelineVarOutputBuilder::GenerateMainFunctionAndIOStructs(compiler, root,
573                                                                              outVarReplacements);
574 }
575 }  // namespace sh
576