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