diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index a338f40413..befa6f63c0 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -1,1261 +1,1262 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace Ryujinx.Graphics.Gal.Shader -{ - public class GlslDecompiler - { - private delegate string GetInstExpr(ShaderIrOp Op); - - private Dictionary InstsExpr; - - private enum OperType - { - Bool, - F32, - I32 - } - - private const string IdentationStr = " "; - - private const int MaxVertexInput = 3; - - private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; - - private GlslDecl Decl; - - private ShaderHeader Header, HeaderB; - - private ShaderIrBlock[] Blocks, BlocksB; - - private StringBuilder SB; - - public GlslDecompiler() - { - InstsExpr = new Dictionary() - { - { ShaderIrInst.Abs, GetAbsExpr }, - { ShaderIrInst.Add, GetAddExpr }, - { ShaderIrInst.And, GetAndExpr }, - { ShaderIrInst.Asr, GetAsrExpr }, - { ShaderIrInst.Band, GetBandExpr }, - { ShaderIrInst.Bnot, GetBnotExpr }, - { ShaderIrInst.Bor, GetBorExpr }, - { ShaderIrInst.Bxor, GetBxorExpr }, - { ShaderIrInst.Ceil, GetCeilExpr }, - { ShaderIrInst.Ceq, GetCeqExpr }, - { ShaderIrInst.Cge, GetCgeExpr }, - { ShaderIrInst.Cgt, GetCgtExpr }, - { ShaderIrInst.Clamps, GetClampsExpr }, - { ShaderIrInst.Clampu, GetClampuExpr }, - { ShaderIrInst.Cle, GetCleExpr }, - { ShaderIrInst.Clt, GetCltExpr }, - { ShaderIrInst.Cne, GetCneExpr }, - { ShaderIrInst.Cut, GetCutExpr }, - { ShaderIrInst.Exit, GetExitExpr }, - { ShaderIrInst.Fabs, GetAbsExpr }, - { ShaderIrInst.Fadd, GetAddExpr }, - { ShaderIrInst.Fceq, GetCeqExpr }, - { ShaderIrInst.Fcequ, GetCequExpr }, - { ShaderIrInst.Fcge, GetCgeExpr }, - { ShaderIrInst.Fcgeu, GetCgeuExpr }, - { ShaderIrInst.Fcgt, GetCgtExpr }, - { ShaderIrInst.Fcgtu, GetCgtuExpr }, - { ShaderIrInst.Fclamp, GetFclampExpr }, - { ShaderIrInst.Fcle, GetCleExpr }, - { ShaderIrInst.Fcleu, GetCleuExpr }, - { ShaderIrInst.Fclt, GetCltExpr }, - { ShaderIrInst.Fcltu, GetCltuExpr }, - { ShaderIrInst.Fcnan, GetCnanExpr }, - { ShaderIrInst.Fcne, GetCneExpr }, - { ShaderIrInst.Fcneu, GetCneuExpr }, - { ShaderIrInst.Fcnum, GetCnumExpr }, - { ShaderIrInst.Fcos, GetFcosExpr }, - { ShaderIrInst.Fex2, GetFex2Expr }, - { ShaderIrInst.Ffma, GetFfmaExpr }, - { ShaderIrInst.Flg2, GetFlg2Expr }, - { ShaderIrInst.Floor, GetFloorExpr }, - { ShaderIrInst.Fmax, GetMaxExpr }, - { ShaderIrInst.Fmin, GetMinExpr }, - { ShaderIrInst.Fmul, GetMulExpr }, - { ShaderIrInst.Fneg, GetNegExpr }, - { ShaderIrInst.Frcp, GetFrcpExpr }, - { ShaderIrInst.Frsq, GetFrsqExpr }, - { ShaderIrInst.Fsin, GetFsinExpr }, - { ShaderIrInst.Fsqrt, GetFsqrtExpr }, - { ShaderIrInst.Ftos, GetFtosExpr }, - { ShaderIrInst.Ftou, GetFtouExpr }, - { ShaderIrInst.Ipa, GetIpaExpr }, - { ShaderIrInst.Kil, GetKilExpr }, - { ShaderIrInst.Lsl, GetLslExpr }, - { ShaderIrInst.Lsr, GetLsrExpr }, - { ShaderIrInst.Max, GetMaxExpr }, - { ShaderIrInst.Min, GetMinExpr }, - { ShaderIrInst.Mul, GetMulExpr }, - { ShaderIrInst.Neg, GetNegExpr }, - { ShaderIrInst.Not, GetNotExpr }, - { ShaderIrInst.Or, GetOrExpr }, - { ShaderIrInst.Stof, GetStofExpr }, - { ShaderIrInst.Sub, GetSubExpr }, - { ShaderIrInst.Texq, GetTexqExpr }, - { ShaderIrInst.Texs, GetTexsExpr }, - { ShaderIrInst.Trunc, GetTruncExpr }, - { ShaderIrInst.Txlf, GetTxlfExpr }, - { ShaderIrInst.Utof, GetUtofExpr }, - { ShaderIrInst.Xor, GetXorExpr } - }; - } - - public GlslProgram Decompile( - IGalMemory Memory, - long VpAPosition, - long VpBPosition, - GalShaderType ShaderType) - { - Header = new ShaderHeader(Memory, VpAPosition); - HeaderB = new ShaderHeader(Memory, VpBPosition); - - Blocks = ShaderDecoder.Decode(Memory, VpAPosition); - BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); - - GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType); - GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType); - - Decl = GlslDecl.Merge(DeclVpA, DeclVpB); - - return Decompile(); - } - - public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) - { - Header = new ShaderHeader(Memory, Position); - HeaderB = null; - - Blocks = ShaderDecoder.Decode(Memory, Position); - BlocksB = null; - - Decl = new GlslDecl(Blocks, ShaderType); - - return Decompile(); - } - - private GlslProgram Decompile() - { - SB = new StringBuilder(); - - SB.AppendLine("#version 410 core"); - - PrintDeclHeader(); - PrintDeclTextures(); - PrintDeclUniforms(); - PrintDeclAttributes(); - PrintDeclInAttributes(); - PrintDeclOutAttributes(); - PrintDeclGprs(); - PrintDeclPreds(); - - if (BlocksB != null) - { - PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramAName + "()", IdentationStr); - - SB.AppendLine(); - - PrintBlockScope(BlocksB[0], null, null, "void " + GlslDecl.ProgramBName + "()", IdentationStr); - } - else - { - PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramName + "()", IdentationStr); - } - - SB.AppendLine(); - - PrintMain(); - - string GlslCode = SB.ToString(); - - return new GlslProgram( - GlslCode, - Decl.Textures.Values, - Decl.Uniforms.Values); - } - - private void PrintDeclHeader() - { - if (Decl.ShaderType == GalShaderType.Geometry) - { - int MaxVertices = Header.MaxOutputVertexCount; - - string OutputTopology; - - switch (Header.OutputTopology) - { - case ShaderHeader.PointList: OutputTopology = "points"; break; - case ShaderHeader.LineStrip: OutputTopology = "line_strip"; break; - case ShaderHeader.TriangleStrip: OutputTopology = "triangle_strip"; break; - - default: throw new InvalidOperationException(); - } - - SB.AppendLine("#extension GL_ARB_enhanced_layouts : require"); - - SB.AppendLine(); - - SB.AppendLine("// Stubbed. Maxwell geometry shaders don't inform input geometry type"); - - SB.AppendLine("layout(triangles) in;" + Environment.NewLine); - - SB.AppendLine($"layout({OutputTopology}, max_vertices = {MaxVertices}) out;"); - - SB.AppendLine(); - } - } - - private void PrintDeclTextures() - { - PrintDecls(Decl.Textures, "uniform sampler2D"); - } - - private void PrintDeclUniforms() - { - if (Decl.ShaderType == GalShaderType.Vertex) - { - SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";"); - } - - SB.AppendLine(); - - foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) - { - SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); - - SB.AppendLine($"{IdentationStr}vec4 {DeclInfo.Name}_data[{GlslDecl.MaxUboSize}];"); - - SB.AppendLine("};"); - } - - if (Decl.Uniforms.Count > 0) - { - SB.AppendLine(); - } - } - - private void PrintDeclAttributes() - { - string GeometryArray = (Decl.ShaderType == GalShaderType.Geometry) ? "[" + MaxVertexInput + "]" : ""; - - PrintDecls(Decl.Attributes, Suffix: GeometryArray); - } - - private void PrintDeclInAttributes() - { - if (Decl.ShaderType == GalShaderType.Fragment) - { - SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") in vec4 " + GlslDecl.PositionOutAttrName + ";"); - } - - if (Decl.ShaderType == GalShaderType.Geometry) - { - if (Decl.InAttributes.Count > 0) - { - SB.AppendLine("in Vertex {"); - - foreach (ShaderDeclInfo DeclInfo in Decl.InAttributes.Values.OrderBy(DeclKeySelector)) - { - if (DeclInfo.Index >= 0) - { - SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") " + GetDecl(DeclInfo) + "; "); - } - } - - SB.AppendLine("} block_in[];" + Environment.NewLine); - } - } - else - { - PrintDeclAttributes(Decl.InAttributes.Values, "in"); - } - } - - private void PrintDeclOutAttributes() - { - if (Decl.ShaderType != GalShaderType.Fragment) - { - SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";"); - } - - PrintDeclAttributes(Decl.OutAttributes.Values, "out"); - } - - private void PrintDeclAttributes(IEnumerable Decls, string InOut) - { - int Count = 0; - - foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) - { - if (DeclInfo.Index >= 0) - { - SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";"); - - Count++; - } - } - - if (Count > 0) - { - SB.AppendLine(); - } - } - - private void PrintDeclGprs() - { - PrintDecls(Decl.Gprs); - } - - private void PrintDeclPreds() - { - PrintDecls(Decl.Preds, "bool"); - } - - private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null, string Suffix = "") - { - foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector)) - { - string Name; - - if (CustomType != null) - { - Name = CustomType + " " + DeclInfo.Name + Suffix + ";"; - } - else if (DeclInfo.Name == GlslDecl.FragmentOutputName) - { - Name = "layout (location = 0) out " + GetDecl(DeclInfo) + Suffix + ";" + Environment.NewLine; - } - else - { - Name = GetDecl(DeclInfo) + Suffix + ";"; - } - - SB.AppendLine(Name); - } - - if (Dict.Count > 0) - { - SB.AppendLine(); - } - } - - private int DeclKeySelector(ShaderDeclInfo DeclInfo) - { - return DeclInfo.Cbuf << 24 | DeclInfo.Index; - } - - private string GetDecl(ShaderDeclInfo DeclInfo) - { - return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name; - } - - private void PrintMain() - { - SB.AppendLine("void main() {"); - - foreach (KeyValuePair KV in Decl.InAttributes) - { - if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) - { - continue; - } - - ShaderDeclInfo DeclInfo = KV.Value; - - string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - - if (Decl.ShaderType == GalShaderType.Geometry) - { - for (int Vertex = 0; Vertex < MaxVertexInput; Vertex++) - { - string Dst = Attr.Name + "[" + Vertex + "]" + Swizzle; - - string Src = "block_in[" + Vertex + "]." + DeclInfo.Name; - - SB.AppendLine(IdentationStr + Dst + " = " + Src + ";"); - } - } - else - { - SB.AppendLine(IdentationStr + Attr.Name + Swizzle + " = " + DeclInfo.Name + ";"); - } - } - - if (BlocksB != null) - { - SB.AppendLine(IdentationStr + GlslDecl.ProgramAName + "();"); - SB.AppendLine(IdentationStr + GlslDecl.ProgramBName + "();"); - } - else - { - SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();"); - } - - if (Decl.ShaderType != GalShaderType.Geometry) - { - PrintAttrToOutput(); - } - - SB.AppendLine("}"); - } - - private void PrintAttrToOutput(string Identation = IdentationStr) - { - foreach (KeyValuePair KV in Decl.OutAttributes) - { - if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) - { - continue; - } - - ShaderDeclInfo DeclInfo = KV.Value; - - string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - - string Name = Attr.Name; - - if (Decl.ShaderType == GalShaderType.Geometry) - { - Name += "[0]"; - } - - SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + Swizzle + ";"); - } - - if (Decl.ShaderType == GalShaderType.Vertex) - { - SB.AppendLine(Identation + "gl_Position.xy *= " + GlslDecl.FlipUniformName + ";"); - } - - if (Decl.ShaderType != GalShaderType.Fragment) - { - SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); - SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); - } - } - - private void PrintBlockScope( - ShaderIrBlock Block, - ShaderIrBlock EndBlock, - ShaderIrBlock LoopBlock, - string ScopeName, - string Identation, - bool IsDoWhile = false) - { - string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length); - - if (IsDoWhile) - { - SB.AppendLine(UpIdent + "do {"); - } - else - { - SB.AppendLine(UpIdent + ScopeName + " {"); - } - - while (Block != null && Block != EndBlock) - { - ShaderIrNode[] Nodes = Block.GetNodes(); - - Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes); - } - - if (IsDoWhile) - { - SB.AppendLine(UpIdent + "} " + ScopeName + ";"); - } - else - { - SB.AppendLine(UpIdent + "}"); - } - } - - private ShaderIrBlock PrintNodes( - ShaderIrBlock Block, - ShaderIrBlock EndBlock, - ShaderIrBlock LoopBlock, - string Identation, - params ShaderIrNode[] Nodes) - { - /* - * Notes about control flow and if-else/loop generation: - * The code assumes that the program has sane control flow, - * that is, there's no jumps to a location after another jump or - * jump target (except for the end of an if-else block), and backwards - * jumps to a location before the last loop dominator. - * Such cases needs to be transformed on a step before the GLSL code - * generation to ensure that we have sane graphs to work with. - * TODO: Such transformation is not yet implemented. - */ - string NewIdent = Identation + IdentationStr; - - ShaderIrBlock LoopTail = GetLoopTailBlock(Block); - - if (LoopTail != null && LoopBlock != Block) - { - //Shoock! kuma shock! We have a loop here! - //The entire sequence needs to be inside a do-while block. - ShaderIrBlock LoopEnd = GetDownBlock(LoopTail); - - PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true); - - return LoopEnd; - } - - foreach (ShaderIrNode Node in Nodes) - { - if (Node is ShaderIrCond Cond) - { - string IfExpr = GetSrcExpr(Cond.Pred, true); - - if (Cond.Not) - { - IfExpr = "!(" + IfExpr + ")"; - } - - if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) - { - //Branch is a loop branch and would result in infinite recursion. - if (Block.Branch.Position <= Block.Position) - { - SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - - SB.AppendLine(Identation + IdentationStr + "continue;"); - - SB.AppendLine(Identation + "}"); - - continue; - } - - string SubScopeName = "if (!" + IfExpr + ")"; - - PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent); - - ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch; - - if (IfElseEnd?.Position > Block.Branch.Position) - { - PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent); - - return IfElseEnd; - } - - return Block.Branch; - } - else - { - SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - - PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child); - - SB.AppendLine(Identation + "}"); - } - } - else if (Node is ShaderIrAsg Asg) - { - if (IsValidOutOper(Asg.Dst)) - { - string Expr = GetSrcExpr(Asg.Src, true); - - Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr); - - SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";"); - } - } - else if (Node is ShaderIrOp Op) - { - if (Op.Inst == ShaderIrInst.Bra) - { - if (Block.Branch.Position <= Block.Position) - { - SB.AppendLine(Identation + "continue;"); - } - } - else if (Op.Inst == ShaderIrInst.Emit) - { - PrintAttrToOutput(Identation); - - SB.AppendLine(Identation + "EmitVertex();"); - } - else - { - SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); - } - } - else if (Node is ShaderIrCmnt Cmnt) - { - SB.AppendLine(Identation + "// " + Cmnt.Comment); - } - else - { - throw new InvalidOperationException(); - } - } - - return Block.Next; - } - - private ShaderIrBlock GetUpBlock(ShaderIrBlock Block) - { - return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position); - } - - private ShaderIrBlock GetDownBlock(ShaderIrBlock Block) - { - return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition); - } - - private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead) - { - ShaderIrBlock Tail = null; - - foreach (ShaderIrBlock Block in LoopHead.Sources) - { - if (Block.Position >= LoopHead.Position) - { - if (Tail == null || Tail.Position < Block.Position) - { - Tail = Block; - } - } - } - - return Tail; - } - - private bool IsValidOutOper(ShaderIrNode Node) - { - if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) - { - return false; - } - else if (Node is ShaderIrOperPred Pred && Pred.IsConst) - { - return false; - } - - return true; - } - - private string GetDstOperName(ShaderIrNode Node) - { - if (Node is ShaderIrOperAbuf Abuf) - { - return GetOutAbufName(Abuf); - } - else if (Node is ShaderIrOperGpr Gpr) - { - return GetName(Gpr); - } - else if (Node is ShaderIrOperPred Pred) - { - return GetName(Pred); - } - - throw new ArgumentException(nameof(Node)); - } - - private string GetSrcExpr(ShaderIrNode Node, bool Entry = false) - { - switch (Node) - { - case ShaderIrOperAbuf Abuf: return GetName (Abuf); - case ShaderIrOperCbuf Cbuf: return GetName (Cbuf); - case ShaderIrOperGpr Gpr: return GetName (Gpr); - case ShaderIrOperImm Imm: return GetValue(Imm); - case ShaderIrOperImmf Immf: return GetValue(Immf); - case ShaderIrOperPred Pred: return GetName (Pred); - - case ShaderIrOp Op: - string Expr; - - if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) - { - Expr = GetExpr(Op); - } - else - { - throw new NotImplementedException(Op.Inst.ToString()); - } - - if (!Entry && NeedsParentheses(Op)) - { - Expr = "(" + Expr + ")"; - } - - return Expr; - - default: throw new ArgumentException(nameof(Node)); - } - } - - private static bool NeedsParentheses(ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Ipa: - case ShaderIrInst.Texq: - case ShaderIrInst.Texs: - case ShaderIrInst.Txlf: - return false; - } - - return true; - } - - private string GetName(ShaderIrOperCbuf Cbuf) - { - if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) - { - throw new InvalidOperationException(); - } - - if (Cbuf.Offs != null) - { - string Offset = "floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ")"; - - string Index = "(" + Cbuf.Pos * 4 + " + " + Offset + ")"; - - return $"{DeclInfo.Name}_data[{Index} / 16][({Index} / 4) % 4]"; - } - else - { - return $"{DeclInfo.Name}_data[{Cbuf.Pos / 4}][{Cbuf.Pos % 4}]"; - } - } - - private string GetOutAbufName(ShaderIrOperAbuf Abuf) - { - if (Decl.ShaderType == GalShaderType.Geometry) - { - switch (Abuf.Offs) - { - case GlslDecl.LayerAttr: return "gl_Layer"; - } - } - - return GetAttrTempName(Abuf); - } - - private string GetName(ShaderIrOperAbuf Abuf) - { - //Handle special scalar read-only attributes here. - if (Decl.ShaderType == GalShaderType.Vertex) - { - switch (Abuf.Offs) - { - case GlslDecl.VertexIdAttr: return "gl_VertexID"; - case GlslDecl.InstanceIdAttr: return "gl_InstanceID"; - } - } - else if (Decl.ShaderType == GalShaderType.TessEvaluation) - { - switch (Abuf.Offs) - { - case GlslDecl.TessCoordAttrX: return "gl_TessCoord.x"; - case GlslDecl.TessCoordAttrY: return "gl_TessCoord.y"; - case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z"; - } - } - else if (Decl.ShaderType == GalShaderType.Fragment) - { - switch (Abuf.Offs) - { - //Note: It's a guess that Maxwell's face is 1 when gl_FrontFacing == true - case GlslDecl.FaceAttr: return "(gl_FrontFacing ? 1 : 0)"; - } - } - - return GetAttrTempName(Abuf); - } - - private string GetAttrTempName(ShaderIrOperAbuf Abuf) - { - int Index = Abuf.Offs >> 4; - int Elem = (Abuf.Offs >> 2) & 3; - - string Swizzle = "." + GetAttrSwizzle(Elem); - - if (!Decl.Attributes.TryGetValue(Index, out ShaderDeclInfo DeclInfo)) - { - //Handle special vec4 attributes here - //(for example, index 7 is aways gl_Position). - if (Index == GlslDecl.GlPositionVec4Index) - { - string Name = - Decl.ShaderType != GalShaderType.Vertex && - Decl.ShaderType != GalShaderType.Geometry ? GlslDecl.PositionOutAttrName : "gl_Position"; - - return Name + Swizzle; - } - - throw new InvalidOperationException(); - } - - if (Decl.ShaderType == GalShaderType.Geometry) - { - string Vertex = "floatBitsToInt(" + GetSrcExpr(Abuf.Vertex) + ")"; - - return DeclInfo.Name + "[" + Vertex + "]" + Swizzle; - } - else - { - return DeclInfo.Name + Swizzle; - } - } - - private string GetName(ShaderIrOperGpr Gpr) - { - return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index); - } - - private string GetValue(ShaderIrOperImm Imm) - { - //Only use hex is the value is too big and would likely be hard to read as int. - if (Imm.Value > 0xfff || - Imm.Value < -0xfff) - { - return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture); - } - else - { - return GetIntConst(Imm.Value); - } - } - - private string GetValue(ShaderIrOperImmf Immf) - { - return GetFloatConst(Immf.Value); - } - - private string GetName(ShaderIrOperPred Pred) - { - return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index); - } - - private string GetNameWithSwizzle(IReadOnlyDictionary Dict, int Index) - { - int VecIndex = Index >> 2; - - if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) - { - if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) - { - return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3); - } - } - - if (!Dict.TryGetValue(Index, out DeclInfo)) - { - throw new InvalidOperationException(); - } - - return DeclInfo.Name; - } - - private string GetAttrSwizzle(int Elem) - { - return "xyzw".Substring(Elem, 1); - } - - private string GetAbsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); - - private string GetAddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); - - private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&"); - - private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>"); - - private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&"); - - private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); - - private string GetBorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "||"); - - private string GetBxorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^^"); - - private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); - - private string GetClampsExpr(ShaderIrOp Op) - { - return "clamp(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ", " + - GetOperExpr(Op, Op.OperandC) + ")"; - } - - private string GetClampuExpr(ShaderIrOp Op) - { - return "int(clamp(uint(" + GetOperExpr(Op, Op.OperandA) + "), " + - "uint(" + GetOperExpr(Op, Op.OperandB) + "), " + - "uint(" + GetOperExpr(Op, Op.OperandC) + ")))"; - } - - private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); - - private string GetCequExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "=="); - - private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">="); - - private string GetCgeuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">="); - - private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); - - private string GetCgtuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">"); - - private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); - - private string GetCleuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<="); - - private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); - - private string GetCltuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<"); - - private string GetCnanExpr(ShaderIrOp Op) => GetUnaryCall(Op, "isnan"); - - private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); - - private string GetCutExpr(ShaderIrOp Op) => "EndPrimitive()"; - - private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!="); - - private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan"); - - private string GetExitExpr(ShaderIrOp Op) => "return"; - - private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); - - private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2"); - - private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+"); - - private string GetFclampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); - - private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); - - private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); - - private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); - - private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt"); - - private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); - - private string GetFsqrtExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sqrt"); - - private string GetFtosExpr(ShaderIrOp Op) - { - return "int(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetFtouExpr(ShaderIrOp Op) - { - return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; - } - - private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA); - - private string GetKilExpr(ShaderIrOp Op) => "discard"; - - private string GetLslExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<<"); - private string GetLsrExpr(ShaderIrOp Op) - { - return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetMaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); - private string GetMinExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); - - private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); - - private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); - - private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~"); - - private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|"); - - private string GetStofExpr(ShaderIrOp Op) - { - return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-"); - - private string GetTexqExpr(ShaderIrOp Op) - { - ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; - - string Ch = "xyzw".Substring(Meta.Elem, 1); - - if (Meta.Info == ShaderTexqInfo.Dimension) - { - string Sampler = GetTexSamplerName(Op); - - string Lod = GetOperExpr(Op, Op.OperandA); //??? - - return "textureSize(" + Sampler + ", " + Lod + ")." + Ch; - } - else - { - throw new NotImplementedException(Meta.Info.ToString()); - } - } - - private string GetTexsExpr(ShaderIrOp Op) - { - ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; - - string Sampler = GetTexSamplerName(Op); - - string Coords = GetTexSamplerCoords(Op); - - string Ch = "rgba".Substring(Meta.Elem, 1); - - return "texture(" + Sampler + ", " + Coords + ")." + Ch; - } - - private string GetTxlfExpr(ShaderIrOp Op) - { - ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; - - string Sampler = GetTexSamplerName(Op); - - string Coords = GetITexSamplerCoords(Op); - - string Ch = "rgba".Substring(Meta.Elem, 1); - - return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; - } - - private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); - - private string GetUtofExpr(ShaderIrOp Op) - { - return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; - } - - private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^"); - - private string GetUnaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetBinaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetTernaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ", " + - GetOperExpr(Op, Op.OperandC) + ")"; - } - - private string GetUnaryExpr(ShaderIrOp Op, string Opr) - { - return Opr + GetOperExpr(Op, Op.OperandA); - } - - private string GetBinaryExpr(ShaderIrOp Op, string Opr) - { - return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " + - GetOperExpr(Op, Op.OperandB); - } - - private string GetBinaryExprWithNaN(ShaderIrOp Op, string Opr) - { - string A = GetOperExpr(Op, Op.OperandA); - string B = GetOperExpr(Op, Op.OperandB); - - string NaNCheck = - " || isnan(" + A + ")" + - " || isnan(" + B + ")"; - - return A + " " + Opr + " " + B + NaNCheck; - } - - private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2) - { - return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " + - GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " + - GetOperExpr(Op, Op.OperandC); - } - - private string GetTexSamplerName(ShaderIrOp Op) - { - ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; - - int Handle = ((ShaderIrOperImm)Op.OperandC).Value; - - if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) - { - throw new InvalidOperationException(); - } - - return DeclInfo.Name; - } - - private string GetTexSamplerCoords(ShaderIrOp Op) - { - return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetITexSamplerCoords(ShaderIrOp Op) - { - return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) - { - return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); - } - - private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr) - { - //Note: The "DstType" (of the cast) is the type that the operation - //uses on the source operands, while the "SrcType" is the destination - //type of the operand result (if it is a operation) or just the type - //of the variable for registers/uniforms/attributes. - OperType DstType = GetSrcNodeType(Dst); - OperType SrcType = GetDstNodeType(Src); - - if (DstType != SrcType) - { - //Check for invalid casts - //(like bool to int/float and others). - if (SrcType != OperType.F32 && - SrcType != OperType.I32) - { - throw new InvalidOperationException(); - } - - switch (Src) - { - case ShaderIrOperGpr Gpr: - { - //When the Gpr is ZR, just return the 0 value directly, - //since the float encoding for 0 is 0. - if (Gpr.IsConst) - { - return "0"; - } - break; - } - - case ShaderIrOperImm Imm: - { - //For integer immediates being used as float, - //it's better (for readability) to just return the float value. - if (DstType == OperType.F32) - { - float Value = BitConverter.Int32BitsToSingle(Imm.Value); - - if (!float.IsNaN(Value) && !float.IsInfinity(Value)) - { - return GetFloatConst(Value); - } - } - break; - } - } - - switch (DstType) - { - case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break; - case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break; - } - } - - return Expr; - } - - private static string GetIntConst(int Value) - { - string Expr = Value.ToString(CultureInfo.InvariantCulture); - - return Value < 0 ? "(" + Expr + ")" : Expr; - } - - private static string GetFloatConst(float Value) - { - string Expr = Value.ToString(CultureInfo.InvariantCulture); - - return Value < 0 ? "(" + Expr + ")" : Expr; - } - - private static OperType GetDstNodeType(ShaderIrNode Node) - { - //Special case instructions with the result type different - //from the input types (like integer <-> float conversion) here. - if (Node is ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Stof: - case ShaderIrInst.Txlf: - case ShaderIrInst.Utof: - return OperType.F32; - - case ShaderIrInst.Ftos: - case ShaderIrInst.Ftou: - return OperType.I32; - } - } - - return GetSrcNodeType(Node); - } - - private static OperType GetSrcNodeType(ShaderIrNode Node) - { - switch (Node) - { - case ShaderIrOperAbuf Abuf: - return Abuf.Offs == GlslDecl.LayerAttr || - Abuf.Offs == GlslDecl.InstanceIdAttr || - Abuf.Offs == GlslDecl.VertexIdAttr || - Abuf.Offs == GlslDecl.FaceAttr - ? OperType.I32 - : OperType.F32; - - case ShaderIrOperCbuf Cbuf: return OperType.F32; - case ShaderIrOperGpr Gpr: return OperType.F32; - case ShaderIrOperImm Imm: return OperType.I32; - case ShaderIrOperImmf Immf: return OperType.F32; - case ShaderIrOperPred Pred: return OperType.Bool; - - case ShaderIrOp Op: - if (Op.Inst > ShaderIrInst.B_Start && - Op.Inst < ShaderIrInst.B_End) - { - return OperType.Bool; - } - else if (Op.Inst > ShaderIrInst.F_Start && - Op.Inst < ShaderIrInst.F_End) - { - return OperType.F32; - } - else if (Op.Inst > ShaderIrInst.I_Start && - Op.Inst < ShaderIrInst.I_End) - { - return OperType.I32; - } - break; - } - - throw new ArgumentException(nameof(Node)); - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class GlslDecompiler + { + private delegate string GetInstExpr(ShaderIrOp Op); + + private Dictionary InstsExpr; + + private enum OperType + { + Bool, + F32, + I32 + } + + private const string IdentationStr = " "; + + private const int MaxVertexInput = 3; + + private GlslDecl Decl; + + private ShaderHeader Header, HeaderB; + + private ShaderIrBlock[] Blocks, BlocksB; + + private StringBuilder SB; + + public GlslDecompiler() + { + InstsExpr = new Dictionary() + { + { ShaderIrInst.Abs, GetAbsExpr }, + { ShaderIrInst.Add, GetAddExpr }, + { ShaderIrInst.And, GetAndExpr }, + { ShaderIrInst.Asr, GetAsrExpr }, + { ShaderIrInst.Band, GetBandExpr }, + { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Bor, GetBorExpr }, + { ShaderIrInst.Bxor, GetBxorExpr }, + { ShaderIrInst.Ceil, GetCeilExpr }, + { ShaderIrInst.Ceq, GetCeqExpr }, + { ShaderIrInst.Cge, GetCgeExpr }, + { ShaderIrInst.Cgt, GetCgtExpr }, + { ShaderIrInst.Clamps, GetClampsExpr }, + { ShaderIrInst.Clampu, GetClampuExpr }, + { ShaderIrInst.Cle, GetCleExpr }, + { ShaderIrInst.Clt, GetCltExpr }, + { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Cut, GetCutExpr }, + { ShaderIrInst.Exit, GetExitExpr }, + { ShaderIrInst.Fabs, GetAbsExpr }, + { ShaderIrInst.Fadd, GetAddExpr }, + { ShaderIrInst.Fceq, GetCeqExpr }, + { ShaderIrInst.Fcequ, GetCequExpr }, + { ShaderIrInst.Fcge, GetCgeExpr }, + { ShaderIrInst.Fcgeu, GetCgeuExpr }, + { ShaderIrInst.Fcgt, GetCgtExpr }, + { ShaderIrInst.Fcgtu, GetCgtuExpr }, + { ShaderIrInst.Fclamp, GetFclampExpr }, + { ShaderIrInst.Fcle, GetCleExpr }, + { ShaderIrInst.Fcleu, GetCleuExpr }, + { ShaderIrInst.Fclt, GetCltExpr }, + { ShaderIrInst.Fcltu, GetCltuExpr }, + { ShaderIrInst.Fcnan, GetCnanExpr }, + { ShaderIrInst.Fcne, GetCneExpr }, + { ShaderIrInst.Fcneu, GetCneuExpr }, + { ShaderIrInst.Fcnum, GetCnumExpr }, + { ShaderIrInst.Fcos, GetFcosExpr }, + { ShaderIrInst.Fex2, GetFex2Expr }, + { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Flg2, GetFlg2Expr }, + { ShaderIrInst.Floor, GetFloorExpr }, + { ShaderIrInst.Fmax, GetMaxExpr }, + { ShaderIrInst.Fmin, GetMinExpr }, + { ShaderIrInst.Fmul, GetMulExpr }, + { ShaderIrInst.Fneg, GetNegExpr }, + { ShaderIrInst.Frcp, GetFrcpExpr }, + { ShaderIrInst.Frsq, GetFrsqExpr }, + { ShaderIrInst.Fsin, GetFsinExpr }, + { ShaderIrInst.Fsqrt, GetFsqrtExpr }, + { ShaderIrInst.Ftos, GetFtosExpr }, + { ShaderIrInst.Ftou, GetFtouExpr }, + { ShaderIrInst.Ipa, GetIpaExpr }, + { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Lsl, GetLslExpr }, + { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Max, GetMaxExpr }, + { ShaderIrInst.Min, GetMinExpr }, + { ShaderIrInst.Mul, GetMulExpr }, + { ShaderIrInst.Neg, GetNegExpr }, + { ShaderIrInst.Not, GetNotExpr }, + { ShaderIrInst.Or, GetOrExpr }, + { ShaderIrInst.Stof, GetStofExpr }, + { ShaderIrInst.Sub, GetSubExpr }, + { ShaderIrInst.Texq, GetTexqExpr }, + { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Trunc, GetTruncExpr }, + { ShaderIrInst.Txlf, GetTxlfExpr }, + { ShaderIrInst.Utof, GetUtofExpr }, + { ShaderIrInst.Xor, GetXorExpr } + }; + } + + public GlslProgram Decompile( + IGalMemory Memory, + long VpAPosition, + long VpBPosition, + GalShaderType ShaderType) + { + Header = new ShaderHeader(Memory, VpAPosition); + HeaderB = new ShaderHeader(Memory, VpBPosition); + + Blocks = ShaderDecoder.Decode(Memory, VpAPosition); + BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); + + GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType); + GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType); + + Decl = GlslDecl.Merge(DeclVpA, DeclVpB); + + return Decompile(); + } + + public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) + { + Header = new ShaderHeader(Memory, Position); + HeaderB = null; + + Blocks = ShaderDecoder.Decode(Memory, Position); + BlocksB = null; + + Decl = new GlslDecl(Blocks, ShaderType); + + return Decompile(); + } + + private GlslProgram Decompile() + { + SB = new StringBuilder(); + + SB.AppendLine("#version 410 core"); + + PrintDeclHeader(); + PrintDeclTextures(); + PrintDeclUniforms(); + PrintDeclAttributes(); + PrintDeclInAttributes(); + PrintDeclOutAttributes(); + PrintDeclGprs(); + PrintDeclPreds(); + + if (BlocksB != null) + { + PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramAName + "()", IdentationStr); + + SB.AppendLine(); + + PrintBlockScope(BlocksB[0], null, null, "void " + GlslDecl.ProgramBName + "()", IdentationStr); + } + else + { + PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramName + "()", IdentationStr); + } + + SB.AppendLine(); + + PrintMain(); + + string GlslCode = SB.ToString(); + + return new GlslProgram( + GlslCode, + Decl.Textures.Values, + Decl.Uniforms.Values); + } + + private void PrintDeclHeader() + { + if (Decl.ShaderType == GalShaderType.Geometry) + { + int MaxVertices = Header.MaxOutputVertexCount; + + string OutputTopology; + + switch (Header.OutputTopology) + { + case ShaderHeader.PointList: OutputTopology = "points"; break; + case ShaderHeader.LineStrip: OutputTopology = "line_strip"; break; + case ShaderHeader.TriangleStrip: OutputTopology = "triangle_strip"; break; + + default: throw new InvalidOperationException(); + } + + SB.AppendLine("#extension GL_ARB_enhanced_layouts : require"); + + SB.AppendLine(); + + SB.AppendLine("// Stubbed. Maxwell geometry shaders don't inform input geometry type"); + + SB.AppendLine("layout(triangles) in;" + Environment.NewLine); + + SB.AppendLine($"layout({OutputTopology}, max_vertices = {MaxVertices}) out;"); + + SB.AppendLine(); + } + } + + private void PrintDeclTextures() + { + PrintDecls(Decl.Textures, "uniform sampler2D"); + } + + private void PrintDeclUniforms() + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";"); + } + + SB.AppendLine(); + + foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) + { + SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); + + SB.AppendLine($"{IdentationStr}vec4 {DeclInfo.Name}_data[{GlslDecl.MaxUboSize}];"); + + SB.AppendLine("};"); + } + + if (Decl.Uniforms.Count > 0) + { + SB.AppendLine(); + } + } + + private void PrintDeclAttributes() + { + string GeometryArray = (Decl.ShaderType == GalShaderType.Geometry) ? "[" + MaxVertexInput + "]" : ""; + + PrintDecls(Decl.Attributes, Suffix: GeometryArray); + } + + private void PrintDeclInAttributes() + { + if (Decl.ShaderType == GalShaderType.Fragment) + { + SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") in vec4 " + GlslDecl.PositionOutAttrName + ";"); + } + + if (Decl.ShaderType == GalShaderType.Geometry) + { + if (Decl.InAttributes.Count > 0) + { + SB.AppendLine("in Vertex {"); + + foreach (ShaderDeclInfo DeclInfo in Decl.InAttributes.Values.OrderBy(DeclKeySelector)) + { + if (DeclInfo.Index >= 0) + { + SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") " + "vec4 " + DeclInfo.Name + "; "); + } + } + + SB.AppendLine("} block_in[];" + Environment.NewLine); + } + } + else + { + PrintDeclAttributes(Decl.InAttributes.Values, "in"); + } + } + + private void PrintDeclOutAttributes() + { + if (Decl.ShaderType != GalShaderType.Fragment) + { + SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";"); + } + + PrintDeclAttributes(Decl.OutAttributes.Values, "out"); + } + + private void PrintDeclAttributes(IEnumerable Decls, string InOut) + { + int Count = 0; + + foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) + { + if (DeclInfo.Index >= 0) + { + SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " vec4 " + DeclInfo.Name + ";"); + + Count++; + } + } + + if (Count > 0) + { + SB.AppendLine(); + } + } + + private void PrintDeclGprs() + { + PrintDecls(Decl.Gprs); + } + + private void PrintDeclPreds() + { + PrintDecls(Decl.Preds, "bool"); + } + + private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null, string Suffix = "") + { + foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector)) + { + string Name; + + if (CustomType != null) + { + Name = CustomType + " " + DeclInfo.Name + Suffix + ";"; + } + else if (DeclInfo.Name == GlslDecl.FragmentOutputName) + { + Name = "layout (location = 0) out " + "vec4 " + DeclInfo.Name + Suffix + ";" + Environment.NewLine; + } + else + { + Name = GetDecl(DeclInfo) + Suffix + ";"; + } + + SB.AppendLine(Name); + } + + if (Dict.Count > 0) + { + SB.AppendLine(); + } + } + + private int DeclKeySelector(ShaderDeclInfo DeclInfo) + { + return DeclInfo.Cbuf << 24 | DeclInfo.Index; + } + + private string GetDecl(ShaderDeclInfo DeclInfo) + { + if (DeclInfo.Size == 4) + { + return "vec4 " + DeclInfo.Name; + } + else + { + return "float " + DeclInfo.Name; + } + } + + private void PrintMain() + { + SB.AppendLine("void main() {"); + + foreach (KeyValuePair KV in Decl.InAttributes) + { + if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) + { + continue; + } + + ShaderDeclInfo DeclInfo = KV.Value; + + if (Decl.ShaderType == GalShaderType.Geometry) + { + for (int Vertex = 0; Vertex < MaxVertexInput; Vertex++) + { + string Dst = Attr.Name + "[" + Vertex + "]"; + + string Src = "block_in[" + Vertex + "]." + DeclInfo.Name; + + SB.AppendLine(IdentationStr + Dst + " = " + Src + ";"); + } + } + else + { + SB.AppendLine(IdentationStr + Attr.Name + " = " + DeclInfo.Name + ";"); + } + } + + if (BlocksB != null) + { + SB.AppendLine(IdentationStr + GlslDecl.ProgramAName + "();"); + SB.AppendLine(IdentationStr + GlslDecl.ProgramBName + "();"); + } + else + { + SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();"); + } + + if (Decl.ShaderType != GalShaderType.Geometry) + { + PrintAttrToOutput(); + } + + SB.AppendLine("}"); + } + + private void PrintAttrToOutput(string Identation = IdentationStr) + { + foreach (KeyValuePair KV in Decl.OutAttributes) + { + if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) + { + continue; + } + + ShaderDeclInfo DeclInfo = KV.Value; + + string Name = Attr.Name; + + if (Decl.ShaderType == GalShaderType.Geometry) + { + Name += "[0]"; + } + + SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + ";"); + } + + if (Decl.ShaderType == GalShaderType.Vertex) + { + SB.AppendLine(Identation + "gl_Position.xy *= " + GlslDecl.FlipUniformName + ";"); + } + + if (Decl.ShaderType != GalShaderType.Fragment) + { + SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); + SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); + } + } + + private void PrintBlockScope( + ShaderIrBlock Block, + ShaderIrBlock EndBlock, + ShaderIrBlock LoopBlock, + string ScopeName, + string Identation, + bool IsDoWhile = false) + { + string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length); + + if (IsDoWhile) + { + SB.AppendLine(UpIdent + "do {"); + } + else + { + SB.AppendLine(UpIdent + ScopeName + " {"); + } + + while (Block != null && Block != EndBlock) + { + ShaderIrNode[] Nodes = Block.GetNodes(); + + Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes); + } + + if (IsDoWhile) + { + SB.AppendLine(UpIdent + "} " + ScopeName + ";"); + } + else + { + SB.AppendLine(UpIdent + "}"); + } + } + + private ShaderIrBlock PrintNodes( + ShaderIrBlock Block, + ShaderIrBlock EndBlock, + ShaderIrBlock LoopBlock, + string Identation, + params ShaderIrNode[] Nodes) + { + /* + * Notes about control flow and if-else/loop generation: + * The code assumes that the program has sane control flow, + * that is, there's no jumps to a location after another jump or + * jump target (except for the end of an if-else block), and backwards + * jumps to a location before the last loop dominator. + * Such cases needs to be transformed on a step before the GLSL code + * generation to ensure that we have sane graphs to work with. + * TODO: Such transformation is not yet implemented. + */ + string NewIdent = Identation + IdentationStr; + + ShaderIrBlock LoopTail = GetLoopTailBlock(Block); + + if (LoopTail != null && LoopBlock != Block) + { + //Shoock! kuma shock! We have a loop here! + //The entire sequence needs to be inside a do-while block. + ShaderIrBlock LoopEnd = GetDownBlock(LoopTail); + + PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true); + + return LoopEnd; + } + + foreach (ShaderIrNode Node in Nodes) + { + if (Node is ShaderIrCond Cond) + { + string IfExpr = GetSrcExpr(Cond.Pred, true); + + if (Cond.Not) + { + IfExpr = "!(" + IfExpr + ")"; + } + + if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) + { + //Branch is a loop branch and would result in infinite recursion. + if (Block.Branch.Position <= Block.Position) + { + SB.AppendLine(Identation + "if (" + IfExpr + ") {"); + + SB.AppendLine(Identation + IdentationStr + "continue;"); + + SB.AppendLine(Identation + "}"); + + continue; + } + + string SubScopeName = "if (!" + IfExpr + ")"; + + PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent); + + ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch; + + if (IfElseEnd?.Position > Block.Branch.Position) + { + PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent); + + return IfElseEnd; + } + + return Block.Branch; + } + else + { + SB.AppendLine(Identation + "if (" + IfExpr + ") {"); + + PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child); + + SB.AppendLine(Identation + "}"); + } + } + else if (Node is ShaderIrAsg Asg) + { + if (IsValidOutOper(Asg.Dst)) + { + string Expr = GetSrcExpr(Asg.Src, true); + + Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr); + + SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";"); + } + } + else if (Node is ShaderIrOp Op) + { + if (Op.Inst == ShaderIrInst.Bra) + { + if (Block.Branch.Position <= Block.Position) + { + SB.AppendLine(Identation + "continue;"); + } + } + else if (Op.Inst == ShaderIrInst.Emit) + { + PrintAttrToOutput(Identation); + + SB.AppendLine(Identation + "EmitVertex();"); + } + else + { + SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); + } + } + else if (Node is ShaderIrCmnt Cmnt) + { + SB.AppendLine(Identation + "// " + Cmnt.Comment); + } + else + { + throw new InvalidOperationException(); + } + } + + return Block.Next; + } + + private ShaderIrBlock GetUpBlock(ShaderIrBlock Block) + { + return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position); + } + + private ShaderIrBlock GetDownBlock(ShaderIrBlock Block) + { + return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition); + } + + private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead) + { + ShaderIrBlock Tail = null; + + foreach (ShaderIrBlock Block in LoopHead.Sources) + { + if (Block.Position >= LoopHead.Position) + { + if (Tail == null || Tail.Position < Block.Position) + { + Tail = Block; + } + } + } + + return Tail; + } + + private bool IsValidOutOper(ShaderIrNode Node) + { + if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) + { + return false; + } + else if (Node is ShaderIrOperPred Pred && Pred.IsConst) + { + return false; + } + + return true; + } + + private string GetDstOperName(ShaderIrNode Node) + { + if (Node is ShaderIrOperAbuf Abuf) + { + return GetOutAbufName(Abuf); + } + else if (Node is ShaderIrOperGpr Gpr) + { + return GetName(Gpr); + } + else if (Node is ShaderIrOperPred Pred) + { + return GetName(Pred); + } + + throw new ArgumentException(nameof(Node)); + } + + private string GetSrcExpr(ShaderIrNode Node, bool Entry = false) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: return GetName(Abuf); + case ShaderIrOperCbuf Cbuf: return GetName(Cbuf); + case ShaderIrOperGpr Gpr: return GetName(Gpr); + case ShaderIrOperImm Imm: return GetValue(Imm); + case ShaderIrOperImmf Immf: return GetValue(Immf); + case ShaderIrOperPred Pred: return GetName(Pred); + + case ShaderIrOp Op: + string Expr; + + if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) + { + Expr = GetExpr(Op); + } + else + { + throw new NotImplementedException(Op.Inst.ToString()); + } + + if (!Entry && NeedsParentheses(Op)) + { + Expr = "(" + Expr + ")"; + } + + return Expr; + + default: throw new ArgumentException(nameof(Node)); + } + } + + private static bool NeedsParentheses(ShaderIrOp Op) + { + switch (Op.Inst) + { + case ShaderIrInst.Ipa: + case ShaderIrInst.Texq: + case ShaderIrInst.Texs: + case ShaderIrInst.Txlf: + return false; + } + + return true; + } + + private string GetName(ShaderIrOperCbuf Cbuf) + { + if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + if (Cbuf.Offs != null) + { + string Offset = "floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ")"; + + string Index = "(" + Cbuf.Pos * 4 + " + " + Offset + ")"; + + return $"{DeclInfo.Name}_data[{Index} / 16][({Index} / 4) % 4]"; + } + else + { + return $"{DeclInfo.Name}_data[{Cbuf.Pos / 4}][{Cbuf.Pos % 4}]"; + } + } + + private string GetOutAbufName(ShaderIrOperAbuf Abuf) + { + if (Decl.ShaderType == GalShaderType.Geometry) + { + switch (Abuf.Offs) + { + case GlslDecl.LayerAttr: return "gl_Layer"; + } + } + + return GetAttrTempName(Abuf); + } + + private string GetName(ShaderIrOperAbuf Abuf) + { + //Handle special scalar read-only attributes here. + if (Decl.ShaderType == GalShaderType.Vertex) + { + switch (Abuf.Offs) + { + case GlslDecl.VertexIdAttr: return "gl_VertexID"; + case GlslDecl.InstanceIdAttr: return "gl_InstanceID"; + } + } + else if (Decl.ShaderType == GalShaderType.TessEvaluation) + { + switch (Abuf.Offs) + { + case GlslDecl.TessCoordAttrX: return "gl_TessCoord.x"; + case GlslDecl.TessCoordAttrY: return "gl_TessCoord.y"; + case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z"; + } + } + else if (Decl.ShaderType == GalShaderType.Fragment) + { + switch (Abuf.Offs) + { + //Note: It's a guess that Maxwell's face is 1 when gl_FrontFacing == true + case GlslDecl.FaceAttr: return "(gl_FrontFacing ? 1 : 0)"; + } + } + + return GetAttrTempName(Abuf); + } + + private string GetAttrTempName(ShaderIrOperAbuf Abuf) + { + int Index = Abuf.Offs >> 4; + int Elem = (Abuf.Offs >> 2) & 3; + + string Swizzle = "." + GetAttrSwizzle(Elem); + + if (!Decl.Attributes.TryGetValue(Index, out ShaderDeclInfo DeclInfo)) + { + //Handle special vec4 attributes here + //(for example, index 7 is aways gl_Position). + if (Index == GlslDecl.GlPositionVec4Index) + { + string Name = + Decl.ShaderType != GalShaderType.Vertex && + Decl.ShaderType != GalShaderType.Geometry ? GlslDecl.PositionOutAttrName : "gl_Position"; + + return Name + Swizzle; + } + + throw new InvalidOperationException(); + } + + if (Decl.ShaderType == GalShaderType.Geometry) + { + string Vertex = "floatBitsToInt(" + GetSrcExpr(Abuf.Vertex) + ")"; + + return DeclInfo.Name + "[" + Vertex + "]" + Swizzle; + } + else + { + return DeclInfo.Name + Swizzle; + } + } + + private string GetName(ShaderIrOperGpr Gpr) + { + return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index); + } + + private string GetValue(ShaderIrOperImm Imm) + { + //Only use hex is the value is too big and would likely be hard to read as int. + if (Imm.Value > 0xfff || + Imm.Value < -0xfff) + { + return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture); + } + else + { + return GetIntConst(Imm.Value); + } + } + + private string GetValue(ShaderIrOperImmf Immf) + { + return GetFloatConst(Immf.Value); + } + + private string GetName(ShaderIrOperPred Pred) + { + return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index); + } + + private string GetNameWithSwizzle(IReadOnlyDictionary Dict, int Index) + { + int VecIndex = Index >> 2; + + if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) + { + if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) + { + return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3); + } + } + + if (!Dict.TryGetValue(Index, out DeclInfo)) + { + throw new InvalidOperationException(); + } + + return DeclInfo.Name; + } + + private string GetAttrSwizzle(int Elem) + { + return "xyzw".Substring(Elem, 1); + } + + private string GetAbsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); + + private string GetAddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); + + private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&"); + + private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>"); + + private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&"); + + private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); + + private string GetBorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "||"); + + private string GetBxorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^^"); + + private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); + + private string GetClampsExpr(ShaderIrOp Op) + { + return "clamp(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ", " + + GetOperExpr(Op, Op.OperandC) + ")"; + } + + private string GetClampuExpr(ShaderIrOp Op) + { + return "int(clamp(uint(" + GetOperExpr(Op, Op.OperandA) + "), " + + "uint(" + GetOperExpr(Op, Op.OperandB) + "), " + + "uint(" + GetOperExpr(Op, Op.OperandC) + ")))"; + } + + private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); + + private string GetCequExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "=="); + + private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">="); + + private string GetCgeuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">="); + + private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); + + private string GetCgtuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">"); + + private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); + + private string GetCleuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<="); + + private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); + + private string GetCltuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<"); + + private string GetCnanExpr(ShaderIrOp Op) => GetUnaryCall(Op, "isnan"); + + private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); + + private string GetCutExpr(ShaderIrOp Op) => "EndPrimitive()"; + + private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!="); + + private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan"); + + private string GetExitExpr(ShaderIrOp Op) => "return"; + + private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); + + private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2"); + + private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+"); + + private string GetFclampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); + + private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); + + private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); + + private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); + + private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt"); + + private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); + + private string GetFsqrtExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sqrt"); + + private string GetFtosExpr(ShaderIrOp Op) + { + return "int(" + GetOperExpr(Op, Op.OperandA) + ")"; + } + + private string GetFtouExpr(ShaderIrOp Op) + { + return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; + } + + private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA); + + private string GetKilExpr(ShaderIrOp Op) => "discard"; + + private string GetLslExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<<"); + private string GetLsrExpr(ShaderIrOp Op) + { + return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + + private string GetMaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); + private string GetMinExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); + + private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); + + private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); + + private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~"); + + private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|"); + + private string GetStofExpr(ShaderIrOp Op) + { + return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; + } + + private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-"); + + private string GetTexqExpr(ShaderIrOp Op) + { + ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; + + string Ch = "xyzw".Substring(Meta.Elem, 1); + + if (Meta.Info == ShaderTexqInfo.Dimension) + { + string Sampler = GetTexSamplerName(Op); + + string Lod = GetOperExpr(Op, Op.OperandA); //??? + + return "textureSize(" + Sampler + ", " + Lod + ")." + Ch; + } + else + { + throw new NotImplementedException(Meta.Info.ToString()); + } + } + + private string GetTexsExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + string Sampler = GetTexSamplerName(Op); + + string Coords = GetTexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return "texture(" + Sampler + ", " + Coords + ")." + Ch; + } + + private string GetTxlfExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + string Sampler = GetTexSamplerName(Op); + + string Coords = GetITexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; + } + + private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); + + private string GetUtofExpr(ShaderIrOp Op) + { + return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; + } + + private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^"); + + private string GetUnaryCall(ShaderIrOp Op, string FuncName) + { + return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; + } + + private string GetBinaryCall(ShaderIrOp Op, string FuncName) + { + return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + + private string GetTernaryCall(ShaderIrOp Op, string FuncName) + { + return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ", " + + GetOperExpr(Op, Op.OperandC) + ")"; + } + + private string GetUnaryExpr(ShaderIrOp Op, string Opr) + { + return Opr + GetOperExpr(Op, Op.OperandA); + } + + private string GetBinaryExpr(ShaderIrOp Op, string Opr) + { + return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " + + GetOperExpr(Op, Op.OperandB); + } + + private string GetBinaryExprWithNaN(ShaderIrOp Op, string Opr) + { + string A = GetOperExpr(Op, Op.OperandA); + string B = GetOperExpr(Op, Op.OperandB); + + string NaNCheck = + " || isnan(" + A + ")" + + " || isnan(" + B + ")"; + + return A + " " + Opr + " " + B + NaNCheck; + } + + private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2) + { + return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " + + GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " + + GetOperExpr(Op, Op.OperandC); + } + + private string GetTexSamplerName(ShaderIrOp Op) + { + ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; + + int Handle = ((ShaderIrOperImm)Op.OperandC).Value; + + if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + return DeclInfo.Name; + } + + private string GetTexSamplerCoords(ShaderIrOp Op) + { + return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + + private string GetITexSamplerCoords(ShaderIrOp Op) + { + return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + + private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) + { + return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); + } + + private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr) + { + //Note: The "DstType" (of the cast) is the type that the operation + //uses on the source operands, while the "SrcType" is the destination + //type of the operand result (if it is a operation) or just the type + //of the variable for registers/uniforms/attributes. + OperType DstType = GetSrcNodeType(Dst); + OperType SrcType = GetDstNodeType(Src); + + if (DstType != SrcType) + { + //Check for invalid casts + //(like bool to int/float and others). + if (SrcType != OperType.F32 && + SrcType != OperType.I32) + { + throw new InvalidOperationException(); + } + + switch (Src) + { + case ShaderIrOperGpr Gpr: + { + //When the Gpr is ZR, just return the 0 value directly, + //since the float encoding for 0 is 0. + if (Gpr.IsConst) + { + return "0"; + } + break; + } + + case ShaderIrOperImm Imm: + { + //For integer immediates being used as float, + //it's better (for readability) to just return the float value. + if (DstType == OperType.F32) + { + float Value = BitConverter.Int32BitsToSingle(Imm.Value); + + if (!float.IsNaN(Value) && !float.IsInfinity(Value)) + { + return GetFloatConst(Value); + } + } + break; + } + } + + switch (DstType) + { + case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break; + case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break; + } + } + + return Expr; + } + + private static string GetIntConst(int Value) + { + string Expr = Value.ToString(CultureInfo.InvariantCulture); + + return Value < 0 ? "(" + Expr + ")" : Expr; + } + + private static string GetFloatConst(float Value) + { + string Expr = Value.ToString(CultureInfo.InvariantCulture); + + return Value < 0 ? "(" + Expr + ")" : Expr; + } + + private static OperType GetDstNodeType(ShaderIrNode Node) + { + //Special case instructions with the result type different + //from the input types (like integer <-> float conversion) here. + if (Node is ShaderIrOp Op) + { + switch (Op.Inst) + { + case ShaderIrInst.Stof: + case ShaderIrInst.Txlf: + case ShaderIrInst.Utof: + return OperType.F32; + + case ShaderIrInst.Ftos: + case ShaderIrInst.Ftou: + return OperType.I32; + } + } + + return GetSrcNodeType(Node); + } + + private static OperType GetSrcNodeType(ShaderIrNode Node) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: + return Abuf.Offs == GlslDecl.LayerAttr || + Abuf.Offs == GlslDecl.InstanceIdAttr || + Abuf.Offs == GlslDecl.VertexIdAttr || + Abuf.Offs == GlslDecl.FaceAttr + ? OperType.I32 + : OperType.F32; + + case ShaderIrOperCbuf Cbuf: return OperType.F32; + case ShaderIrOperGpr Gpr: return OperType.F32; + case ShaderIrOperImm Imm: return OperType.I32; + case ShaderIrOperImmf Immf: return OperType.F32; + case ShaderIrOperPred Pred: return OperType.Bool; + + case ShaderIrOp Op: + if (Op.Inst > ShaderIrInst.B_Start && + Op.Inst < ShaderIrInst.B_End) + { + return OperType.Bool; + } + else if (Op.Inst > ShaderIrInst.F_Start && + Op.Inst < ShaderIrInst.F_End) + { + return OperType.F32; + } + else if (Op.Inst > ShaderIrInst.I_Start && + Op.Inst < ShaderIrInst.I_End) + { + return OperType.I32; + } + break; + } + + throw new ArgumentException(nameof(Node)); + } + } +}