mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-24 08:42:03 +00:00
1f554c1093
* 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
1679 lines
57 KiB
C#
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";
|
|
}
|
|
}
|
|
}
|
|
}
|