Ryujinx/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Alex Barney 1f554c1093 Do naming refactoring on Ryujinx.Graphics (#611)
* Renaming part 1

* Renaming part 2

* Renaming part 3

* Renaming part 4

* Renaming part 5

* Renaming part 6

* Renaming part 7

* Renaming part 8

* Renaming part 9

* Renaming part 10

* General cleanup

* Thought I got all of these

* Apply #595

* Additional renaming

* Tweaks from feedback

* Rename files
2019-03-04 12:45:25 +11:00

1679 lines
57 KiB
C#

using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
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<ShaderIrInst, GetInstExpr> _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 int MaxUboSize { get; }
private bool _isNvidiaDriver;
public GlslDecompiler(int maxUboSize, bool isNvidiaDriver)
{
_instsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
{
{ 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.Texb, GetTexbExpr },
{ ShaderIrInst.Texq, GetTexqExpr },
{ ShaderIrInst.Texs, GetTexsExpr },
{ ShaderIrInst.Tld4, GetTld4Expr },
{ ShaderIrInst.Trunc, GetTruncExpr },
{ ShaderIrInst.Txlf, GetTxlfExpr },
{ ShaderIrInst.Utof, GetUtofExpr },
{ ShaderIrInst.Xor, GetXorExpr }
};
MaxUboSize = maxUboSize / 16;
_isNvidiaDriver = isNvidiaDriver;
}
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, _header);
GlslDecl declVpB = new GlslDecl(_blocksB, shaderType, _headerB);
_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, _header);
return Decompile();
}
private GlslProgram Decompile()
{
_sb = new StringBuilder();
_sb.AppendLine("#version 410 core");
PrintDeclHeader();
PrintDeclTextures();
PrintDeclUniforms();
PrintDeclAttributes();
PrintDeclInAttributes();
PrintDeclOutAttributes();
PrintDeclGprs();
PrintDeclPreds();
PrintDeclSsy();
if (_blocksB != null)
{
PrintBlockScope(_blocks, GlslDecl.BasicBlockAName);
_sb.AppendLine();
PrintBlockScope(_blocksB, GlslDecl.BasicBlockBName);
}
else
{
PrintBlockScope(_blocks, GlslDecl.BasicBlockName);
}
_sb.AppendLine();
PrintMain();
string glslCode = _sb.ToString();
List<ShaderDeclInfo> textureInfo = new List<ShaderDeclInfo>();
textureInfo.AddRange(_decl.Textures.Values);
textureInfo.AddRange(IterateCbTextures());
return new GlslProgram(glslCode, textureInfo, _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 string GetSamplerType(TextureTarget textureTarget, bool hasShadow)
{
string result;
switch (textureTarget)
{
case TextureTarget.Texture1D:
result = "sampler1D";
break;
case TextureTarget.Texture2D:
result = "sampler2D";
break;
case TextureTarget.Texture3D:
result = "sampler3D";
break;
case TextureTarget.TextureCubeMap:
result = "samplerCube";
break;
case TextureTarget.TextureRectangle:
result = "sampler2DRect";
break;
case TextureTarget.Texture1DArray:
result = "sampler1DArray";
break;
case TextureTarget.Texture2DArray:
result = "sampler2DArray";
break;
case TextureTarget.TextureCubeMapArray:
result = "samplerCubeArray";
break;
case TextureTarget.TextureBuffer:
result = "samplerBuffer";
break;
case TextureTarget.Texture2DMultisample:
result = "sampler2DMS";
break;
case TextureTarget.Texture2DMultisampleArray:
result = "sampler2DMSArray";
break;
default:
throw new NotSupportedException();
}
if (hasShadow)
result += "Shadow";
return result;
}
private void PrintDeclTextures()
{
foreach (ShaderDeclInfo declInfo in IterateCbTextures())
{
TextureTarget target = ImageUtils.GetTextureTarget(declInfo.TextureTarget);
_sb.AppendLine($"// {declInfo.TextureSuffix}");
_sb.AppendLine("uniform " + GetSamplerType(target, (declInfo.TextureSuffix & TextureInstructionSuffix.Dc) != 0) + " " + declInfo.Name + ";");
}
foreach (ShaderDeclInfo declInfo in _decl.Textures.Values.OrderBy(DeclKeySelector))
{
TextureTarget target = ImageUtils.GetTextureTarget(declInfo.TextureTarget);
_sb.AppendLine($"// {declInfo.TextureSuffix}");
_sb.AppendLine("uniform " + GetSamplerType(target, (declInfo.TextureSuffix & TextureInstructionSuffix.Dc) != 0) + " " + declInfo.Name + ";");
}
}
private IEnumerable<ShaderDeclInfo> IterateCbTextures()
{
HashSet<string> names = new HashSet<string>();
foreach (ShaderDeclInfo declInfo in _decl.CbTextures.Values.OrderBy(DeclKeySelector))
{
if (names.Add(declInfo.Name))
{
yield return declInfo;
}
}
}
private void PrintDeclUniforms()
{
if (_decl.ShaderType == GalShaderType.Vertex)
{
//Memory layout here is [flip_x, flip_y, instance, unused]
//It's using 4 bytes, not 8
_sb.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + " {");
_sb.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";");
_sb.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";");
_sb.AppendLine("};");
_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[{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)
{
int count = 0;
for (int attachment = 0; attachment < 8; attachment++)
{
if (_header.OmapTargets[attachment].Enabled)
{
_sb.AppendLine("layout (location = " + attachment + ") out vec4 " + GlslDecl.FragmentOutputName + attachment + ";");
count++;
}
}
if (count > 0)
{
_sb.AppendLine();
}
}
else
{
_sb.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";");
_sb.AppendLine();
}
PrintDeclAttributes(_decl.OutAttributes.Values, "out");
}
private void PrintDeclAttributes(IEnumerable<ShaderDeclInfo> 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);
PrintDecls(_decl.GprsHalf);
}
private void PrintDeclPreds()
{
PrintDecls(_decl.Preds, "bool");
}
private void PrintDeclSsy()
{
_sb.AppendLine("uint " + GlslDecl.SsyCursorName + " = 0;");
_sb.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine);
}
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> 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.Contains(GlslDecl.FragmentOutputName))
{
name = "layout (location = " + declInfo.Index / 4 + ") out vec4 " + declInfo.Name + suffix + ";";
}
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<int, ShaderDeclInfo> 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 + ";");
}
}
_sb.AppendLine(IdentationStr + "uint pc;");
if (_blocksB != null)
{
PrintProgram(_blocks, GlslDecl.BasicBlockAName);
PrintProgram(_blocksB, GlslDecl.BasicBlockBName);
}
else
{
PrintProgram(_blocks, GlslDecl.BasicBlockName);
}
if (_decl.ShaderType != GalShaderType.Geometry)
{
PrintAttrToOutput();
}
if (_decl.ShaderType == GalShaderType.Fragment)
{
if (_header.OmapDepth)
{
_sb.AppendLine(IdentationStr + "gl_FragDepth = " + GlslDecl.GetGprName(_header.DepthRegister) + ";");
}
int gprIndex = 0;
for (int attachment = 0; attachment < 8; attachment++)
{
string output = GlslDecl.FragmentOutputName + attachment;
OmapTarget target = _header.OmapTargets[attachment];
for (int component = 0; component < 4; component++)
{
if (target.ComponentEnabled(component))
{
_sb.AppendLine(IdentationStr + output + "[" + component + "] = " + GlslDecl.GetGprName(gprIndex) + ";");
gprIndex++;
}
}
}
}
_sb.AppendLine("}");
}
private void PrintProgram(ShaderIrBlock[] blocks, string name)
{
const string ident1 = IdentationStr;
const string ident2 = ident1 + IdentationStr;
const string ident3 = ident2 + IdentationStr;
const string ident4 = ident3 + IdentationStr;
_sb.AppendLine(ident1 + "pc = " + GetBlockPosition(blocks[0]) + ";");
_sb.AppendLine(ident1 + "do {");
_sb.AppendLine(ident2 + "switch (pc) {");
foreach (ShaderIrBlock block in blocks)
{
string functionName = block.Position.ToString("x8");
_sb.AppendLine(ident3 + "case 0x" + functionName + ": pc = " + name + "_" + functionName + "(); break;");
}
_sb.AppendLine(ident3 + "default:");
_sb.AppendLine(ident4 + "pc = 0;");
_sb.AppendLine(ident4 + "break;");
_sb.AppendLine(ident2 + "}");
_sb.AppendLine(ident1 + "} while (pc != 0);");
}
private void PrintAttrToOutput(string identation = IdentationStr)
{
foreach (KeyValuePair<int, ShaderDeclInfo> 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[] blocks, string name)
{
foreach (ShaderIrBlock block in blocks)
{
_sb.AppendLine("uint " + name + "_" + block.Position.ToString("x8") + "() {");
PrintNodes(block, block.GetNodes());
_sb.AppendLine("}" + Environment.NewLine);
}
}
private void PrintNodes(ShaderIrBlock block, ShaderIrNode[] nodes)
{
foreach (ShaderIrNode node in nodes)
{
PrintNode(block, node, IdentationStr);
}
if (nodes.Length == 0)
{
_sb.AppendLine(IdentationStr + "return 0u;");
return;
}
ShaderIrNode last = nodes[nodes.Length - 1];
bool unconditionalFlowChange = false;
if (last is ShaderIrOp op)
{
switch (op.Inst)
{
case ShaderIrInst.Bra:
case ShaderIrInst.Exit:
case ShaderIrInst.Sync:
unconditionalFlowChange = true;
break;
}
}
if (!unconditionalFlowChange)
{
if (block.Next != null)
{
_sb.AppendLine(IdentationStr + "return " + GetBlockPosition(block.Next) + ";");
}
else
{
_sb.AppendLine(IdentationStr + "return 0u;");
}
}
}
private void PrintNode(ShaderIrBlock block, ShaderIrNode node, string identation)
{
if (node is ShaderIrCond cond)
{
string ifExpr = GetSrcExpr(cond.Pred, true);
if (cond.Not)
{
ifExpr = "!(" + ifExpr + ")";
}
_sb.AppendLine(identation + "if (" + ifExpr + ") {");
PrintNode(block, cond.Child, identation + IdentationStr);
_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)
{
switch (op.Inst)
{
case ShaderIrInst.Bra:
{
_sb.AppendLine(identation + "return " + GetBlockPosition(block.Branch) + ";");
break;
}
case ShaderIrInst.Emit:
{
PrintAttrToOutput(identation);
_sb.AppendLine(identation + "EmitVertex();");
break;
}
case ShaderIrInst.Ssy:
{
string stackIndex = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
int targetPosition = (op.OperandA as ShaderIrOperImm).Value;
string target = "0x" + targetPosition.ToString("x8") + "u";
_sb.AppendLine(identation + stackIndex + " = " + target + ";");
_sb.AppendLine(identation + GlslDecl.SsyCursorName + "++;");
break;
}
case ShaderIrInst.Sync:
{
_sb.AppendLine(identation + GlslDecl.SsyCursorName + "--;");
string target = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
_sb.AppendLine(identation + "return " + target + ";");
break;
}
default:
_sb.AppendLine(identation + GetSrcExpr(op, true) + ";");
break;
}
}
else if (node is ShaderIrCmnt cmnt)
{
_sb.AppendLine(identation + "// " + cmnt.Comment);
}
else
{
throw new InvalidOperationException();
}
}
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.Tld4:
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 GlslDecl.InstanceUniformName;
}
}
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)
{
case GlslDecl.PointCoordAttrX: return "gl_PointCoord.x";
case GlslDecl.PointCoordAttrY: return "gl_PointCoord.y";
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 always gl_Position).
if (index == GlslDecl.GlPositionVec4Index)
{
string name =
_decl.ShaderType != GalShaderType.Vertex &&
_decl.ShaderType != GalShaderType.Geometry ? GlslDecl.PositionOutAttrName : "gl_Position";
return name + swizzle;
}
else if (abuf.Offs == GlslDecl.PointSizeAttr)
{
return "gl_PointSize";
}
}
if (declInfo.Index >= 32)
{
throw new InvalidOperationException($"Shader attribute offset {abuf.Offs} is invalid.");
}
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)
{
if (gpr.IsConst)
{
return "0";
}
if (gpr.RegisterSize == ShaderRegisterSize.Single)
{
return GetNameWithSwizzle(_decl.Gprs, gpr.Index);
}
else if (gpr.RegisterSize == ShaderRegisterSize.Half)
{
return GetNameWithSwizzle(_decl.GprsHalf, (gpr.Index << 1) | gpr.HalfPart);
}
else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */
{
throw new NotImplementedException("Double types are not supported.");
}
}
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<int, ShaderDeclInfo> dict, int index)
{
int vecIndex = index & ~3;
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 0u";
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)
{
ShaderIrMetaIpa meta = (ShaderIrMetaIpa)op.MetaData;
ShaderIrOperAbuf abuf = (ShaderIrOperAbuf)op.OperandA;
if (meta.Mode == ShaderIpaMode.Pass)
{
int index = abuf.Offs >> 4;
int elem = (abuf.Offs >> 2) & 3;
if (_decl.ShaderType == GalShaderType.Fragment && index == GlslDecl.GlPositionVec4Index)
{
switch (elem)
{
case 0: return "gl_FragCoord.x";
case 1: return "gl_FragCoord.y";
case 2: return "gl_FragCoord.z";
case 3: return "1";
}
}
}
return 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 GetTexbExpr(ShaderIrOp op)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
if (!_decl.CbTextures.TryGetValue(op, out ShaderDeclInfo declInfo))
{
throw new InvalidOperationException();
}
string coords = GetTexSamplerCoords(op);
string ch = "rgba".Substring(meta.Elem, 1);
return GetTextureOperation(op, declInfo.Name, coords, ch);
}
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 GetTextureOperation(op, sampler, coords, ch);
}
private string GetTld4Expr(ShaderIrOp op)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
string sampler = GetTexSamplerName(op);
string coords = GetTexSamplerCoords(op);
string ch = "rgba".Substring(meta.Elem, 1);
return GetTextureGatherOperation(op, sampler, coords, ch);
}
// TODO: support AOFFI on non nvidia drivers
private string GetTxlfExpr(ShaderIrOp op)
{
// TODO: Support all suffixes
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
TextureInstructionSuffix suffix = meta.TextureInstructionSuffix;
string sampler = GetTexSamplerName(op);
string coords = GetITexSamplerCoords(op);
string ch = "rgba".Substring(meta.Elem, 1);
string lod = "0";
if (meta.LevelOfDetail != null)
{
lod = GetOperExpr(op, meta.LevelOfDetail);
}
if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, GetOperExpr(op, meta.Offset));
return "texelFetchOffset(" + sampler + ", " + coords + ", " + lod + ", " + offset + ")." + ch;
}
return "texelFetch(" + sampler + ", " + coords + ", " + lod + ")." + 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)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
bool hasDepth = (meta.TextureInstructionSuffix & TextureInstructionSuffix.Dc) != 0;
int coords = ImageUtils.GetCoordsCountTextureTarget(meta.TextureTarget);
bool isArray = ImageUtils.IsArray(meta.TextureTarget);
string GetLastArgument(ShaderIrNode node)
{
string result = GetOperExpr(op, node);
// array index is actually an integer so we need to pass it correctly
if (isArray)
{
result = "float(floatBitsToInt(" + result + "))";
}
return result;
}
string lastArgument;
string depthArgument = "";
int vecSize = coords;
if (hasDepth && op.Inst != ShaderIrInst.Tld4)
{
vecSize++;
depthArgument = $", {GetOperExpr(op, meta.DepthCompare)}";
}
switch (coords)
{
case 1:
if (hasDepth)
{
return $"vec3({GetOperExpr(op, meta.Coordinates[0])}, 0.0{depthArgument})";
}
return GetOperExpr(op, meta.Coordinates[0]);
case 2:
lastArgument = GetLastArgument(meta.Coordinates[1]);
return $"vec{vecSize}({GetOperExpr(op, meta.Coordinates[0])}, {lastArgument}{depthArgument})";
case 3:
lastArgument = GetLastArgument(meta.Coordinates[2]);
return $"vec{vecSize}({GetOperExpr(op, meta.Coordinates[0])}, {GetOperExpr(op, meta.Coordinates[1])}, {lastArgument}{depthArgument})";
case 4:
lastArgument = GetLastArgument(meta.Coordinates[3]);
return $"vec4({GetOperExpr(op, meta.Coordinates[0])}, {GetOperExpr(op, meta.Coordinates[1])}, {GetOperExpr(op, meta.Coordinates[2])}, {lastArgument}){depthArgument}";
default:
throw new InvalidOperationException();
}
}
private string GetTextureOffset(ShaderIrMetaTex meta, string oper, int shift = 4, int mask = 0xF)
{
string GetOffset(string operation, int index)
{
return $"({operation} >> {index * shift}) & 0x{mask:x}";
}
int coords = ImageUtils.GetCoordsCountTextureTarget(meta.TextureTarget);
if (ImageUtils.IsArray(meta.TextureTarget))
coords -= 1;
switch (coords)
{
case 1:
return GetOffset(oper, 0);
case 2:
return "ivec2(" + GetOffset(oper, 0) + ", " + GetOffset(oper, 1) + ")";
case 3:
return "ivec3(" + GetOffset(oper, 0) + ", " + GetOffset(oper, 1) + ", " + GetOffset(oper, 2) + ")";
case 4:
return "ivec4(" + GetOffset(oper, 0) + ", " + GetOffset(oper, 1) + ", " + GetOffset(oper, 2) + ", " + GetOffset(oper, 3) + ")";
default:
throw new InvalidOperationException();
}
}
// TODO: support AOFFI on non nvidia drivers
private string GetTextureGatherOperation(ShaderIrOp op, string sampler, string coords, string ch)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
TextureInstructionSuffix suffix = meta.TextureInstructionSuffix;
string chString = "." + ch;
string comp = meta.Component.ToString();
if ((suffix & TextureInstructionSuffix.Dc) != 0)
{
comp = GetOperExpr(op, meta.DepthCompare);
}
if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, "floatBitsToInt((" + GetOperExpr(op, meta.Offset) + "))", 8, 0x3F);
if ((suffix & TextureInstructionSuffix.Dc) != 0)
{
return "textureGatherOffset(" + sampler + ", " + coords + ", " + comp + ", " + offset + ")" + chString;
}
return "textureGatherOffset(" + sampler + ", " + coords + ", " + offset + ", " + comp + ")" + chString;
}
// TODO: Support PTP
else if ((suffix & TextureInstructionSuffix.Ptp) != 0)
{
throw new NotImplementedException();
}
return "textureGather(" + sampler + ", " + coords + ", " + comp + ")" + chString;
}
// TODO: support AOFFI on non nvidia drivers
private string GetTextureOperation(ShaderIrOp op, string sampler, string coords, string ch)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
TextureInstructionSuffix suffix = meta.TextureInstructionSuffix;
string chString = "." + ch;
if ((suffix & TextureInstructionSuffix.Dc) != 0)
{
chString = "";
}
// TODO: Support LBA and LLA
if ((suffix & TextureInstructionSuffix.Lz) != 0)
{
if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, "floatBitsToInt((" + GetOperExpr(op, meta.Offset) + "))");
return "textureLodOffset(" + sampler + ", " + coords + ", 0.0, " + offset + ")" + chString;
}
return "textureLod(" + sampler + ", " + coords + ", 0.0)" + chString;
}
else if ((suffix & TextureInstructionSuffix.Lb) != 0)
{
if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, "floatBitsToInt((" + GetOperExpr(op, meta.Offset) + "))");
return "textureOffset(" + sampler + ", " + coords + ", " + offset + ", " + GetOperExpr(op, meta.LevelOfDetail) + ")" + chString;
}
return "texture(" + sampler + ", " + coords + ", " + GetOperExpr(op, meta.LevelOfDetail) + ")" + chString;
}
else if ((suffix & TextureInstructionSuffix.Ll) != 0)
{
if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, "floatBitsToInt((" + GetOperExpr(op, meta.Offset) + "))");
return "textureLodOffset(" + sampler + ", " + coords + ", " + GetOperExpr(op, meta.LevelOfDetail) + ", " + offset + ")" + chString;
}
return "textureLod(" + sampler + ", " + coords + ", " + GetOperExpr(op, meta.LevelOfDetail) + ")" + chString;
}
else if ((suffix & TextureInstructionSuffix.AOffI) != 0 && _isNvidiaDriver)
{
string offset = GetTextureOffset(meta, "floatBitsToInt((" + GetOperExpr(op, meta.Offset) + "))");
return "textureOffset(" + sampler + ", " + coords + ", " + offset + ")" + chString;
}
else
{
return "texture(" + sampler + ", " + coords + ")" + chString;
}
throw new NotImplementedException($"Texture Suffix {meta.TextureInstructionSuffix} is not implemented");
}
private string GetITexSamplerCoords(ShaderIrOp op)
{
ShaderIrMetaTex meta = (ShaderIrMetaTex)op.MetaData;
switch (ImageUtils.GetCoordsCountTextureTarget(meta.TextureTarget))
{
case 1:
return GetOperExpr(op, meta.Coordinates[0]);
case 2:
return "ivec2(" + GetOperExpr(op, meta.Coordinates[0]) + ", " + GetOperExpr(op, meta.Coordinates[1]) + ")";
case 3:
return "ivec3(" + GetOperExpr(op, meta.Coordinates[0]) + ", " + GetOperExpr(op, meta.Coordinates[1]) + ", " + GetOperExpr(op, meta.Coordinates[2]) + ")";
default:
throw new InvalidOperationException();
}
}
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;
}
}
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));
}
private static string GetBlockPosition(ShaderIrBlock block)
{
if (block != null)
{
return "0x" + block.Position.ToString("x8") + "u";
}
else
{
return "0u";
}
}
}
}