/ src / Ryujinx.Graphics.Shader / CodeGen / Glsl / GlslGenerator.cs
GlslGenerator.cs
  1  using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
  2  using Ryujinx.Graphics.Shader.StructuredIr;
  3  using Ryujinx.Graphics.Shader.Translation;
  4  using System;
  5  
  6  using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion;
  7  
  8  namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
  9  {
 10      static class GlslGenerator
 11      {
 12          private const string MainFunctionName = "main";
 13  
 14          public static string Generate(StructuredProgramInfo info, CodeGenParameters parameters)
 15          {
 16              CodeGenContext context = new(info, parameters);
 17  
 18              Declarations.Declare(context, info);
 19  
 20              if (info.Functions.Count != 0)
 21              {
 22                  for (int i = 1; i < info.Functions.Count; i++)
 23                  {
 24                      context.AppendLine($"{GetFunctionSignature(context, info.Functions[i])};");
 25                  }
 26  
 27                  context.AppendLine();
 28  
 29                  for (int i = 1; i < info.Functions.Count; i++)
 30                  {
 31                      PrintFunction(context, info.Functions[i]);
 32  
 33                      context.AppendLine();
 34                  }
 35              }
 36  
 37              PrintFunction(context, info.Functions[0], MainFunctionName);
 38  
 39              return context.GetCode();
 40          }
 41  
 42          private static void PrintFunction(CodeGenContext context, StructuredFunction function, string funcName = null)
 43          {
 44              context.CurrentFunction = function;
 45  
 46              context.AppendLine(GetFunctionSignature(context, function, funcName));
 47              context.EnterScope();
 48  
 49              Declarations.DeclareLocals(context, function);
 50  
 51              PrintBlock(context, function.MainBlock, funcName == MainFunctionName);
 52  
 53              context.LeaveScope();
 54          }
 55  
 56          private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, string funcName = null)
 57          {
 58              string[] args = new string[function.InArguments.Length + function.OutArguments.Length];
 59  
 60              for (int i = 0; i < function.InArguments.Length; i++)
 61              {
 62                  args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}";
 63              }
 64  
 65              for (int i = 0; i < function.OutArguments.Length; i++)
 66              {
 67                  int j = i + function.InArguments.Length;
 68  
 69                  args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}";
 70              }
 71  
 72              return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
 73          }
 74  
 75          private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction)
 76          {
 77              AstBlockVisitor visitor = new(block);
 78  
 79              visitor.BlockEntered += (sender, e) =>
 80              {
 81                  switch (e.Block.Type)
 82                  {
 83                      case AstBlockType.DoWhile:
 84                          context.AppendLine("do");
 85                          break;
 86  
 87                      case AstBlockType.Else:
 88                          context.AppendLine("else");
 89                          break;
 90  
 91                      case AstBlockType.ElseIf:
 92                          context.AppendLine($"else if ({GetCondExpr(context, e.Block.Condition)})");
 93                          break;
 94  
 95                      case AstBlockType.If:
 96                          context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})");
 97                          break;
 98  
 99                      default:
100                          throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\".");
101                  }
102  
103                  context.EnterScope();
104              };
105  
106              visitor.BlockLeft += (sender, e) =>
107              {
108                  context.LeaveScope();
109  
110                  if (e.Block.Type == AstBlockType.DoWhile)
111                  {
112                      context.AppendLine($"while ({GetCondExpr(context, e.Block.Condition)});");
113                  }
114              };
115  
116              bool supportsBarrierDivergence = context.HostCapabilities.SupportsShaderBarrierDivergence;
117              bool mayHaveReturned = false;
118  
119              foreach (IAstNode node in visitor.Visit())
120              {
121                  if (node is AstOperation operation)
122                  {
123                      if (!supportsBarrierDivergence)
124                      {
125                          if (operation.Inst == IntermediateRepresentation.Instruction.Barrier)
126                          {
127                              // Barrier on divergent control flow paths may cause the GPU to hang,
128                              // so skip emitting the barrier for those cases.
129                              if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction)
130                              {
131                                  context.Logger.Log("Shader has barrier on potentially divergent block, the barrier will be removed.");
132  
133                                  continue;
134                              }
135                          }
136                          else if (operation.Inst == IntermediateRepresentation.Instruction.Return)
137                          {
138                              mayHaveReturned = true;
139                          }
140                      }
141  
142                      string expr = InstGen.GetExpression(context, operation);
143  
144                      if (expr != null)
145                      {
146                          context.AppendLine(expr + ";");
147                      }
148                  }
149                  else if (node is AstAssignment assignment)
150                  {
151                      AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination);
152                      AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
153  
154                      string dest = InstGen.GetExpression(context, assignment.Destination);
155                      string src = ReinterpretCast(context, assignment.Source, srcType, dstType);
156  
157                      context.AppendLine(dest + " = " + src + ";");
158                  }
159                  else if (node is AstComment comment)
160                  {
161                      context.AppendLine("// " + comment.Comment);
162                  }
163                  else
164                  {
165                      throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\".");
166                  }
167              }
168          }
169  
170          private static string GetCondExpr(CodeGenContext context, IAstNode cond)
171          {
172              AggregateType srcType = OperandManager.GetNodeDestType(context, cond);
173  
174              return ReinterpretCast(context, cond, srcType, AggregateType.Bool);
175          }
176      }
177  }