Add TXQ, TLD, TLDS and TLD4S shader texture instructions, and some support for bindless textures, some refactoring on codegen
This commit is contained in:
parent
ce71abeec9
commit
9f4e21369e
35 changed files with 1388 additions and 907 deletions
|
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
public static void Declare(CodeGenContext context, StructuredProgramInfo prgInfo)
|
||||
{
|
||||
context.AppendLine("#version 410 core");
|
||||
context.AppendLine("#version 420 core");
|
||||
|
||||
context.AppendLine();
|
||||
|
||||
|
@ -110,13 +110,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo prgInfo)
|
||||
{
|
||||
foreach (KeyValuePair<int, TextureType> kv in prgInfo.Samplers.OrderBy(x => x.Key))
|
||||
HashSet<string> samplerNames = new HashSet<string>();
|
||||
|
||||
foreach (AstTextureOperation texOp in prgInfo.Samplers.OrderBy(x => x.Handle))
|
||||
{
|
||||
int textureHandle = kv.Key;
|
||||
string samplerName = OperandManager.GetSamplerName(context.ShaderType, texOp);
|
||||
|
||||
string samplerTypeName = GetSamplerTypeName(kv.Value);
|
||||
if (!samplerNames.Add(samplerName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string samplerName = OperandManager.GetSamplerName(context.ShaderType, textureHandle);
|
||||
string samplerTypeName = GetSamplerTypeName(texOp.Type);
|
||||
|
||||
context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";");
|
||||
}
|
||||
|
@ -154,6 +159,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
||||
}
|
||||
|
||||
if ((type & TextureType.Multisample) != 0)
|
||||
{
|
||||
typeName += "MS";
|
||||
}
|
||||
|
||||
if ((type & TextureType.Array) != 0)
|
||||
{
|
||||
typeName += "Array";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
|
@ -82,7 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
PrepareForReturn(context);
|
||||
}
|
||||
|
||||
context.AppendLine(Instructions.GetExpression(context, operation) + ";");
|
||||
context.AppendLine(InstGen.GetExpression(context, operation) + ";");
|
||||
}
|
||||
else if (node is AstAssignment assignment)
|
||||
{
|
||||
|
@ -97,7 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
else
|
||||
{
|
||||
dest = Instructions.GetExpression(context, assignment.Destination);
|
||||
dest = InstGen.GetExpression(context, assignment.Destination);
|
||||
}
|
||||
|
||||
string src = ReinterpretCast(context, assignment.Source, srcType, dstType);
|
||||
|
|
|
@ -1,515 +0,0 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion;
|
||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
static class Instructions
|
||||
{
|
||||
[Flags]
|
||||
private enum InstFlags
|
||||
{
|
||||
OpNullary = 0,
|
||||
OpUnary = 1,
|
||||
OpBinary = 2,
|
||||
OpTernary = 3,
|
||||
|
||||
CallNullary = Call | 0,
|
||||
CallUnary = Call | 1,
|
||||
CallBinary = Call | 2,
|
||||
CallTernary = Call | 3,
|
||||
CallQuaternary = Call | 4,
|
||||
|
||||
Call = 1 << 8,
|
||||
|
||||
ArityMask = 0xff
|
||||
}
|
||||
|
||||
private struct InstInfo
|
||||
{
|
||||
public InstFlags Flags { get; }
|
||||
|
||||
public string OpName { get; }
|
||||
|
||||
public int Precedence { get; }
|
||||
|
||||
public InstInfo(InstFlags flags, string opName, int precedence)
|
||||
{
|
||||
Flags = flags;
|
||||
OpName = opName;
|
||||
Precedence = precedence;
|
||||
}
|
||||
}
|
||||
|
||||
private static InstInfo[] _infoTbl;
|
||||
|
||||
static Instructions()
|
||||
{
|
||||
_infoTbl = new InstInfo[(int)Instruction.Count];
|
||||
|
||||
Add(Instruction.Absolute, InstFlags.CallUnary, "abs");
|
||||
Add(Instruction.Add, InstFlags.OpBinary, "+", 2);
|
||||
Add(Instruction.BitfieldExtractS32, InstFlags.CallTernary, "bitfieldExtract");
|
||||
Add(Instruction.BitfieldExtractU32, InstFlags.CallTernary, "bitfieldExtract");
|
||||
Add(Instruction.BitfieldInsert, InstFlags.CallQuaternary, "bitfieldInsert");
|
||||
Add(Instruction.BitfieldReverse, InstFlags.CallUnary, "bitfieldReverse");
|
||||
Add(Instruction.BitwiseAnd, InstFlags.OpBinary, "&", 6);
|
||||
Add(Instruction.BitwiseExclusiveOr, InstFlags.OpBinary, "^", 7);
|
||||
Add(Instruction.BitwiseNot, InstFlags.OpUnary, "~", 0);
|
||||
Add(Instruction.BitwiseOr, InstFlags.OpBinary, "|", 8);
|
||||
Add(Instruction.Ceiling, InstFlags.CallUnary, "ceil");
|
||||
Add(Instruction.Clamp, InstFlags.CallTernary, "clamp");
|
||||
Add(Instruction.ClampU32, InstFlags.CallTernary, "clamp");
|
||||
Add(Instruction.CompareEqual, InstFlags.OpBinary, "==", 5);
|
||||
Add(Instruction.CompareGreater, InstFlags.OpBinary, ">", 4);
|
||||
Add(Instruction.CompareGreaterOrEqual, InstFlags.OpBinary, ">=", 4);
|
||||
Add(Instruction.CompareGreaterOrEqualU32, InstFlags.OpBinary, ">=", 4);
|
||||
Add(Instruction.CompareGreaterU32, InstFlags.OpBinary, ">", 4);
|
||||
Add(Instruction.CompareLess, InstFlags.OpBinary, "<", 4);
|
||||
Add(Instruction.CompareLessOrEqual, InstFlags.OpBinary, "<=", 4);
|
||||
Add(Instruction.CompareLessOrEqualU32, InstFlags.OpBinary, "<=", 4);
|
||||
Add(Instruction.CompareLessU32, InstFlags.OpBinary, "<", 4);
|
||||
Add(Instruction.CompareNotEqual, InstFlags.OpBinary, "!=", 5);
|
||||
Add(Instruction.ConditionalSelect, InstFlags.OpTernary, "?:", 12);
|
||||
Add(Instruction.ConvertFPToS32, InstFlags.CallUnary, "int");
|
||||
Add(Instruction.ConvertS32ToFP, InstFlags.CallUnary, "float");
|
||||
Add(Instruction.ConvertU32ToFP, InstFlags.CallUnary, "float");
|
||||
Add(Instruction.Cosine, InstFlags.CallUnary, "cos");
|
||||
Add(Instruction.Discard, InstFlags.OpNullary, "discard");
|
||||
Add(Instruction.Divide, InstFlags.OpBinary, "/", 1);
|
||||
Add(Instruction.EmitVertex, InstFlags.CallNullary, "EmitVertex");
|
||||
Add(Instruction.EndPrimitive, InstFlags.CallNullary, "EndPrimitive");
|
||||
Add(Instruction.ExponentB2, InstFlags.CallUnary, "exp2");
|
||||
Add(Instruction.Floor, InstFlags.CallUnary, "floor");
|
||||
Add(Instruction.FusedMultiplyAdd, InstFlags.CallTernary, "fma");
|
||||
Add(Instruction.IsNan, InstFlags.CallUnary, "isnan");
|
||||
Add(Instruction.LoadConstant, InstFlags.Call);
|
||||
Add(Instruction.LogarithmB2, InstFlags.CallUnary, "log2");
|
||||
Add(Instruction.LogicalAnd, InstFlags.OpBinary, "&&", 9);
|
||||
Add(Instruction.LogicalExclusiveOr, InstFlags.OpBinary, "^^", 10);
|
||||
Add(Instruction.LogicalNot, InstFlags.OpUnary, "!", 0);
|
||||
Add(Instruction.LogicalOr, InstFlags.OpBinary, "||", 11);
|
||||
Add(Instruction.LoopBreak, InstFlags.OpNullary, "break");
|
||||
Add(Instruction.LoopContinue, InstFlags.OpNullary, "continue");
|
||||
Add(Instruction.ShiftLeft, InstFlags.OpBinary, "<<", 3);
|
||||
Add(Instruction.ShiftRightS32, InstFlags.OpBinary, ">>", 3);
|
||||
Add(Instruction.ShiftRightU32, InstFlags.OpBinary, ">>", 3);
|
||||
Add(Instruction.Maximum, InstFlags.CallBinary, "max");
|
||||
Add(Instruction.MaximumU32, InstFlags.CallBinary, "max");
|
||||
Add(Instruction.Minimum, InstFlags.CallBinary, "min");
|
||||
Add(Instruction.MinimumU32, InstFlags.CallBinary, "min");
|
||||
Add(Instruction.Multiply, InstFlags.OpBinary, "*", 1);
|
||||
Add(Instruction.Negate, InstFlags.OpUnary, "-", 0);
|
||||
Add(Instruction.PackHalf2x16, InstFlags.Call);
|
||||
Add(Instruction.ReciprocalSquareRoot, InstFlags.CallUnary, "inversesqrt");
|
||||
Add(Instruction.Return, InstFlags.OpNullary, "return");
|
||||
Add(Instruction.Sine, InstFlags.CallUnary, "sin");
|
||||
Add(Instruction.SquareRoot, InstFlags.CallUnary, "sqrt");
|
||||
Add(Instruction.Subtract, InstFlags.OpBinary, "-", 2);
|
||||
Add(Instruction.TextureSample, InstFlags.Call);
|
||||
Add(Instruction.Truncate, InstFlags.CallUnary, "trunc");
|
||||
Add(Instruction.UnpackHalf2x16High, InstFlags.Call);
|
||||
Add(Instruction.UnpackHalf2x16Low, InstFlags.Call);
|
||||
}
|
||||
|
||||
private static void Add(Instruction inst, InstFlags flags, string opName = null, int precedence = 0)
|
||||
{
|
||||
_infoTbl[(int)inst] = new InstInfo(flags, opName, precedence);
|
||||
}
|
||||
|
||||
public static string GetExpression(CodeGenContext context, IAstNode node)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
return GetExpression(context, operation);
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
switch (operand.Type)
|
||||
{
|
||||
case OperandType.Attribute:
|
||||
return OperandManager.GetAttributeName(context, operand);
|
||||
|
||||
case OperandType.Constant:
|
||||
return NumberFormatter.FormatInt(operand.Value);
|
||||
|
||||
case OperandType.ConstantBuffer:
|
||||
return OperandManager.GetConstantBufferName(context.ShaderType, operand);
|
||||
|
||||
case OperandType.LocalVariable:
|
||||
return context.GetLocalName(operand);
|
||||
|
||||
case OperandType.Undefined:
|
||||
return DefaultNames.UndefinedName;
|
||||
}
|
||||
|
||||
return DefaultNames.UndefinedName;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
||||
}
|
||||
|
||||
private static string GetExpression(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
Instruction inst = operation.Inst & Instruction.Mask;
|
||||
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.LoadConstant:
|
||||
return GetLoadConstantExpr(context, operation);
|
||||
|
||||
case Instruction.PackHalf2x16:
|
||||
return GetPackHalf2x16Expr(context, operation);
|
||||
|
||||
case Instruction.TextureSample:
|
||||
return GetTextureSampleExpr(context, operation);
|
||||
|
||||
case Instruction.UnpackHalf2x16High:
|
||||
return GetUnpackHalf2x16Expr(context, operation, 1);
|
||||
|
||||
case Instruction.UnpackHalf2x16Low:
|
||||
return GetUnpackHalf2x16Expr(context, operation, 0);
|
||||
}
|
||||
|
||||
InstInfo info = _infoTbl[(int)inst];
|
||||
|
||||
if (info.OpName == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown instruction \"{inst}\".");
|
||||
}
|
||||
|
||||
if ((info.Flags & InstFlags.Call) != 0)
|
||||
{
|
||||
int arity = (int)(info.Flags & InstFlags.ArityMask);
|
||||
|
||||
string args = string.Empty;
|
||||
|
||||
for (int argIndex = 0; argIndex < arity; argIndex++)
|
||||
{
|
||||
if (argIndex != 0)
|
||||
{
|
||||
args += ", ";
|
||||
}
|
||||
|
||||
VariableType dstType = GetSrcVarType(operation.Inst, argIndex);
|
||||
|
||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
}
|
||||
|
||||
return info.OpName + "(" + args + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info.Flags == InstFlags.OpNullary)
|
||||
{
|
||||
return info.OpName;
|
||||
}
|
||||
else if (info.Flags == InstFlags.OpUnary)
|
||||
{
|
||||
IAstNode src = operation.GetSource(0);
|
||||
|
||||
string expr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
|
||||
|
||||
return info.OpName + Enclose(expr, src, info);
|
||||
}
|
||||
else if (info.Flags == InstFlags.OpBinary)
|
||||
{
|
||||
IAstNode src0 = operation.GetSource(0);
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
|
||||
string expr0 = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
|
||||
string expr1 = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
expr0 = Enclose(expr0, src0, info, isLhs: true);
|
||||
expr1 = Enclose(expr1, src1, info, isLhs: false);
|
||||
|
||||
return expr0 + " " + info.OpName + " " + expr1;
|
||||
}
|
||||
else if (info.Flags == InstFlags.OpTernary)
|
||||
{
|
||||
IAstNode src0 = operation.GetSource(0);
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
IAstNode src2 = operation.GetSource(2);
|
||||
|
||||
string expr0 = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
|
||||
string expr1 = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
string expr2 = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 2));
|
||||
|
||||
expr0 = Enclose(expr0, src0, info);
|
||||
expr1 = Enclose(expr1, src1, info);
|
||||
expr2 = Enclose(expr2, src2, info);
|
||||
|
||||
char op0 = info.OpName[0];
|
||||
char op1 = info.OpName[1];
|
||||
|
||||
return expr0 + " " + op0 + " " + expr1 + " " + op1 + " " + expr2;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected instruction flags \"{info.Flags}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLoadConstantExpr(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
|
||||
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
offsetExpr = Enclose(offsetExpr, src1, Instruction.ShiftRightS32, isLhs: true);
|
||||
|
||||
return OperandManager.GetConstantBufferName(context, operation.GetSource(0), offsetExpr);
|
||||
}
|
||||
|
||||
private static string GetPackHalf2x16Expr(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src0 = operation.GetSource(0);
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
|
||||
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
|
||||
string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
return $"packHalf2x16(vec2({src0Expr}, {src1Expr}))";
|
||||
}
|
||||
|
||||
private static string GetUnpackHalf2x16Expr(CodeGenContext context, AstOperation operation, int elem)
|
||||
{
|
||||
IAstNode src = operation.GetSource(0);
|
||||
|
||||
string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
|
||||
|
||||
return $"unpackHalf2x16({srcExpr}).{"xy".Substring(elem, 1)}";
|
||||
}
|
||||
|
||||
private static string GetTextureSampleExpr(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
|
||||
bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
|
||||
bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
|
||||
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
||||
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
||||
bool isArray = (texOp.Type & TextureType.Array) != 0;
|
||||
bool isShadow = (texOp.Type & TextureType.Shadow) != 0;
|
||||
|
||||
string samplerName = OperandManager.GetSamplerName(context.ShaderType, texOp.TextureHandle);
|
||||
|
||||
string texCall = "texture";
|
||||
|
||||
if (isGather)
|
||||
{
|
||||
texCall += "Gather";
|
||||
}
|
||||
|
||||
if (hasLodLevel)
|
||||
{
|
||||
texCall += "Lod";
|
||||
}
|
||||
|
||||
if (hasOffset)
|
||||
{
|
||||
texCall += "Offset";
|
||||
}
|
||||
else if (hasOffsets)
|
||||
{
|
||||
texCall += "Offsets";
|
||||
}
|
||||
|
||||
texCall += "(" + samplerName;
|
||||
|
||||
TextureType baseType = texOp.Type & TextureType.Mask;
|
||||
|
||||
int elemsCount;
|
||||
|
||||
switch (baseType)
|
||||
{
|
||||
case TextureType.Texture1D: elemsCount = 1; break;
|
||||
case TextureType.Texture2D: elemsCount = 2; break;
|
||||
case TextureType.Texture3D: elemsCount = 3; break;
|
||||
case TextureType.TextureCube: elemsCount = 3; break;
|
||||
|
||||
default: throw new InvalidOperationException($"Invalid texture type \"{baseType}\".");
|
||||
}
|
||||
|
||||
int pCount = elemsCount;
|
||||
|
||||
int arrayIndexElem = -1;
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
arrayIndexElem = pCount++;
|
||||
}
|
||||
|
||||
if (isShadow && !isGather)
|
||||
{
|
||||
pCount++;
|
||||
}
|
||||
|
||||
//On textureGather*, the comparison value is always specified as an extra argument.
|
||||
bool hasExtraCompareArg = isShadow && isGather;
|
||||
|
||||
if (pCount == 5)
|
||||
{
|
||||
pCount = 4;
|
||||
|
||||
hasExtraCompareArg = true;
|
||||
}
|
||||
|
||||
int srcIndex = 0;
|
||||
|
||||
string Src(VariableType type)
|
||||
{
|
||||
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
|
||||
}
|
||||
|
||||
string AssembleVector(int count, VariableType type, bool isP = false)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
string[] vecElems = new string[count];
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
if (isP && index == arrayIndexElem)
|
||||
{
|
||||
vecElems[index] = "float(" + Src(VariableType.S32) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
vecElems[index] = Src(type);
|
||||
}
|
||||
}
|
||||
|
||||
string prefix = type == VariableType.F32 ? string.Empty : "i";
|
||||
|
||||
return prefix + "vec" + count + "(" + string.Join(", ", vecElems) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return Src(type);
|
||||
}
|
||||
}
|
||||
|
||||
texCall += ", " + AssembleVector(pCount, VariableType.F32, isP: true);
|
||||
|
||||
if (hasExtraCompareArg)
|
||||
{
|
||||
texCall += ", " + Src(VariableType.F32);
|
||||
}
|
||||
|
||||
if (hasLodLevel)
|
||||
{
|
||||
texCall += ", " + Src(VariableType.F32);
|
||||
}
|
||||
|
||||
if (hasOffset)
|
||||
{
|
||||
texCall += ", " + AssembleVector(elemsCount, VariableType.S32);
|
||||
}
|
||||
else if (hasOffsets)
|
||||
{
|
||||
const int gatherTexelsCount = 4;
|
||||
|
||||
texCall += $", ivec{elemsCount}[{gatherTexelsCount}](";
|
||||
|
||||
for (int index = 0; index < gatherTexelsCount; index++)
|
||||
{
|
||||
texCall += AssembleVector(elemsCount, VariableType.S32);
|
||||
|
||||
if (index < gatherTexelsCount - 1)
|
||||
{
|
||||
texCall += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
texCall += ")";
|
||||
}
|
||||
|
||||
if (hasLodBias && !hasLodLevel)
|
||||
{
|
||||
texCall += ", " + Src(VariableType.F32);
|
||||
}
|
||||
|
||||
//textureGather* optional extra component index, not needed for shadow samplers.
|
||||
if (isGather && !isShadow)
|
||||
{
|
||||
texCall += ", " + Src(VariableType.S32);
|
||||
}
|
||||
|
||||
texCall += ")";
|
||||
|
||||
if (isGather || !isShadow)
|
||||
{
|
||||
texCall += ".";
|
||||
|
||||
for (int compIndex = 0; compIndex < texOp.Components.Length; compIndex++)
|
||||
{
|
||||
texCall += "rgba".Substring(texOp.Components[compIndex], 1);
|
||||
}
|
||||
}
|
||||
|
||||
return texCall;
|
||||
}
|
||||
|
||||
private static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType)
|
||||
{
|
||||
return ReinterpretCast(context, node, OperandManager.GetNodeDestType(node), dstType);
|
||||
}
|
||||
|
||||
public static string Enclose(string expr, IAstNode node, Instruction inst, bool isLhs)
|
||||
{
|
||||
InstInfo info = _infoTbl[(int)(inst & Instruction.Mask)];
|
||||
|
||||
return Enclose(expr, node, info, isLhs);
|
||||
}
|
||||
|
||||
private static string Enclose(string expr, IAstNode node, InstInfo pInfo, bool isLhs = false)
|
||||
{
|
||||
if (NeedsParenthesis(node, pInfo, isLhs))
|
||||
{
|
||||
expr = "(" + expr + ")";
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private static bool NeedsParenthesis(IAstNode node, InstInfo pInfo, bool isLhs)
|
||||
{
|
||||
//If the node isn't a operation, then it can only be a operand,
|
||||
//and those never needs to be surrounded in parenthesis.
|
||||
if (!(node is AstOperation operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((pInfo.Flags & InstFlags.Call) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstInfo info = _infoTbl[(int)(operation.Inst & Instruction.Mask)];
|
||||
|
||||
if ((info.Flags & InstFlags.Call) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.Precedence < pInfo.Precedence)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.Precedence == pInfo.Precedence && isLhs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
128
Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs
Normal file
128
Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
static class InstGen
|
||||
{
|
||||
public static string GetExpression(CodeGenContext context, IAstNode node)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
return GetExpression(context, operation);
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
switch (operand.Type)
|
||||
{
|
||||
case OperandType.Attribute:
|
||||
return OperandManager.GetAttributeName(context, operand);
|
||||
|
||||
case OperandType.Constant:
|
||||
return NumberFormatter.FormatInt(operand.Value);
|
||||
|
||||
case OperandType.ConstantBuffer:
|
||||
return OperandManager.GetConstantBufferName(context.ShaderType, operand);
|
||||
|
||||
case OperandType.LocalVariable:
|
||||
return context.GetLocalName(operand);
|
||||
|
||||
case OperandType.Undefined:
|
||||
return DefaultNames.UndefinedName;
|
||||
}
|
||||
|
||||
return DefaultNames.UndefinedName;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
||||
}
|
||||
|
||||
private static string GetExpression(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
Instruction inst = operation.Inst;
|
||||
|
||||
InstInfo info = GetInstructionInfo(inst);
|
||||
|
||||
if ((info.Type & InstType.Call) != 0)
|
||||
{
|
||||
int arity = (int)(info.Type & InstType.ArityMask);
|
||||
|
||||
string args = string.Empty;
|
||||
|
||||
for (int argIndex = 0; argIndex < arity; argIndex++)
|
||||
{
|
||||
if (argIndex != 0)
|
||||
{
|
||||
args += ", ";
|
||||
}
|
||||
|
||||
VariableType dstType = GetSrcVarType(inst, argIndex);
|
||||
|
||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
}
|
||||
|
||||
return info.OpName + "(" + args + ")";
|
||||
}
|
||||
else if ((info.Type & InstType.Op) != 0)
|
||||
{
|
||||
string op = info.OpName;
|
||||
|
||||
int arity = (int)(info.Type & InstType.ArityMask);
|
||||
|
||||
string[] expr = new string[arity];
|
||||
|
||||
for (int index = 0; index < arity; index++)
|
||||
{
|
||||
IAstNode src = operation.GetSource(index);
|
||||
|
||||
string srcExpr = GetSoureExpr(context, src, GetSrcVarType(inst, index));
|
||||
|
||||
bool isLhs = arity == 2 && index == 0;
|
||||
|
||||
expr[index] = Enclose(srcExpr, src, inst, info, isLhs);
|
||||
}
|
||||
|
||||
switch (arity)
|
||||
{
|
||||
case 0:
|
||||
return op;
|
||||
|
||||
case 1:
|
||||
return op + expr[0];
|
||||
|
||||
case 2:
|
||||
return $"{expr[0]} {op} {expr[1]}";
|
||||
|
||||
case 3:
|
||||
return $"{expr[0]} {op[0]} {expr[1]} {op[1]} {expr[2]}";
|
||||
}
|
||||
}
|
||||
else if ((info.Type & InstType.Special) != 0)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.LoadConstant:
|
||||
return InstGenMemory.LoadConstant(context, operation);
|
||||
|
||||
case Instruction.PackHalf2x16:
|
||||
return InstGenPacking.PackHalf2x16(context, operation);
|
||||
|
||||
case Instruction.TextureSample:
|
||||
return InstGenMemory.TextureSample(context, operation);
|
||||
|
||||
case Instruction.TextureSize:
|
||||
return InstGenMemory.TextureSize(context, operation);
|
||||
|
||||
case Instruction.UnpackHalf2x16:
|
||||
return InstGenPacking.UnpackHalf2x16(context, operation);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\".");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
static class InstGenHelper
|
||||
{
|
||||
private static InstInfo[] _infoTbl;
|
||||
|
||||
static InstGenHelper()
|
||||
{
|
||||
_infoTbl = new InstInfo[(int)Instruction.Count];
|
||||
|
||||
Add(Instruction.Absolute, InstType.CallUnary, "abs");
|
||||
Add(Instruction.Add, InstType.OpBinaryCom, "+", 2);
|
||||
Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "bitfieldExtract");
|
||||
Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "bitfieldExtract");
|
||||
Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "bitfieldInsert");
|
||||
Add(Instruction.BitfieldReverse, InstType.CallUnary, "bitfieldReverse");
|
||||
Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&", 6);
|
||||
Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^", 7);
|
||||
Add(Instruction.BitwiseNot, InstType.OpUnary, "~", 0);
|
||||
Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|", 8);
|
||||
Add(Instruction.Ceiling, InstType.CallUnary, "ceil");
|
||||
Add(Instruction.Clamp, InstType.CallTernary, "clamp");
|
||||
Add(Instruction.ClampU32, InstType.CallTernary, "clamp");
|
||||
Add(Instruction.CompareEqual, InstType.OpBinaryCom, "==", 5);
|
||||
Add(Instruction.CompareGreater, InstType.OpBinary, ">", 4);
|
||||
Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">=", 4);
|
||||
Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">=", 4);
|
||||
Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">", 4);
|
||||
Add(Instruction.CompareLess, InstType.OpBinary, "<", 4);
|
||||
Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<=", 4);
|
||||
Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<=", 4);
|
||||
Add(Instruction.CompareLessU32, InstType.OpBinary, "<", 4);
|
||||
Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!=", 5);
|
||||
Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12);
|
||||
Add(Instruction.ConvertFPToS32, InstType.CallUnary, "int");
|
||||
Add(Instruction.ConvertS32ToFP, InstType.CallUnary, "float");
|
||||
Add(Instruction.ConvertU32ToFP, InstType.CallUnary, "float");
|
||||
Add(Instruction.Cosine, InstType.CallUnary, "cos");
|
||||
Add(Instruction.Discard, InstType.OpNullary, "discard");
|
||||
Add(Instruction.Divide, InstType.OpBinary, "/", 1);
|
||||
Add(Instruction.EmitVertex, InstType.CallNullary, "EmitVertex");
|
||||
Add(Instruction.EndPrimitive, InstType.CallNullary, "EndPrimitive");
|
||||
Add(Instruction.ExponentB2, InstType.CallUnary, "exp2");
|
||||
Add(Instruction.Floor, InstType.CallUnary, "floor");
|
||||
Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma");
|
||||
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
||||
Add(Instruction.LoadConstant, InstType.Special);
|
||||
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
||||
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
||||
Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^^", 10);
|
||||
Add(Instruction.LogicalNot, InstType.OpUnary, "!", 0);
|
||||
Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||", 11);
|
||||
Add(Instruction.LoopBreak, InstType.OpNullary, "break");
|
||||
Add(Instruction.LoopContinue, InstType.OpNullary, "continue");
|
||||
Add(Instruction.PackHalf2x16, InstType.Special);
|
||||
Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3);
|
||||
Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3);
|
||||
Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3);
|
||||
Add(Instruction.Maximum, InstType.CallBinary, "max");
|
||||
Add(Instruction.MaximumU32, InstType.CallBinary, "max");
|
||||
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
||||
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
||||
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
||||
Add(Instruction.Negate, InstType.OpUnary, "-", 0);
|
||||
Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "inversesqrt");
|
||||
Add(Instruction.Return, InstType.OpNullary, "return");
|
||||
Add(Instruction.Sine, InstType.CallUnary, "sin");
|
||||
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
||||
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
||||
Add(Instruction.TextureSample, InstType.Special);
|
||||
Add(Instruction.TextureSize, InstType.Special);
|
||||
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
||||
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
||||
}
|
||||
|
||||
private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0)
|
||||
{
|
||||
_infoTbl[(int)inst] = new InstInfo(flags, opName, precedence);
|
||||
}
|
||||
|
||||
public static InstInfo GetInstructionInfo(Instruction inst)
|
||||
{
|
||||
return _infoTbl[(int)(inst & Instruction.Mask)];
|
||||
}
|
||||
|
||||
public static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType)
|
||||
{
|
||||
return ReinterpretCast(context, node, OperandManager.GetNodeDestType(node), dstType);
|
||||
}
|
||||
|
||||
public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs)
|
||||
{
|
||||
InstInfo pInfo = GetInstructionInfo(pInst);
|
||||
|
||||
return Enclose(expr, node, pInst, pInfo, isLhs);
|
||||
}
|
||||
|
||||
public static string Enclose(string expr, IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs = false)
|
||||
{
|
||||
if (NeedsParenthesis(node, pInst, pInfo, isLhs))
|
||||
{
|
||||
expr = "(" + expr + ")";
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs)
|
||||
{
|
||||
//If the node isn't a operation, then it can only be a operand,
|
||||
//and those never needs to be surrounded in parenthesis.
|
||||
if (!(node is AstOperation operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((pInfo.Type & (InstType.Call | InstType.Special)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstInfo info = _infoTbl[(int)(operation.Inst & Instruction.Mask)];
|
||||
|
||||
if ((info.Type & (InstType.Call | InstType.Special)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.Precedence < pInfo.Precedence)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.Precedence == pInfo.Precedence && isLhs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pInst == operation.Inst && (info.Type & InstType.Comutative) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
static class InstGenMemory
|
||||
{
|
||||
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
|
||||
string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
offsetExpr = Enclose(offsetExpr, src1, Instruction.ShiftRightS32, isLhs: true);
|
||||
|
||||
return OperandManager.GetConstantBufferName(context, operation.GetSource(0), offsetExpr);
|
||||
}
|
||||
|
||||
public static string TextureSample(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
|
||||
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||
bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
|
||||
bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
|
||||
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
||||
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
||||
bool isArray = (texOp.Type & TextureType.Array) != 0;
|
||||
bool isMultisample = (texOp.Type & TextureType.Multisample) != 0;
|
||||
bool isShadow = (texOp.Type & TextureType.Shadow) != 0;
|
||||
|
||||
string samplerName = OperandManager.GetSamplerName(context.ShaderType, texOp);
|
||||
|
||||
string texCall = intCoords ? "texelFetch" : "texture";
|
||||
|
||||
if (isGather)
|
||||
{
|
||||
texCall += "Gather";
|
||||
}
|
||||
else if (hasLodLevel && !intCoords)
|
||||
{
|
||||
texCall += "Lod";
|
||||
}
|
||||
|
||||
if (hasOffset)
|
||||
{
|
||||
texCall += "Offset";
|
||||
}
|
||||
else if (hasOffsets)
|
||||
{
|
||||
texCall += "Offsets";
|
||||
}
|
||||
|
||||
texCall += "(" + samplerName;
|
||||
|
||||
int coordsCount = texOp.Type.GetCoordsCount();
|
||||
|
||||
int pCount = coordsCount;
|
||||
|
||||
int arrayIndexElem = -1;
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
arrayIndexElem = pCount++;
|
||||
}
|
||||
|
||||
if (isShadow && !isGather)
|
||||
{
|
||||
pCount++;
|
||||
}
|
||||
|
||||
//On textureGather*, the comparison value is
|
||||
//always specified as an extra argument.
|
||||
bool hasExtraCompareArg = isShadow && isGather;
|
||||
|
||||
if (pCount == 5)
|
||||
{
|
||||
pCount = 4;
|
||||
|
||||
hasExtraCompareArg = true;
|
||||
}
|
||||
|
||||
int srcIndex = isBindless ? 1 : 0;
|
||||
|
||||
string Src(VariableType type)
|
||||
{
|
||||
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
|
||||
}
|
||||
|
||||
void Append(string str)
|
||||
{
|
||||
texCall += ", " + str;
|
||||
}
|
||||
|
||||
string AssembleVector(int count, VariableType type)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
string[] vecElems = new string[count];
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
if (arrayIndexElem == index)
|
||||
{
|
||||
arrayIndexElem = -1;
|
||||
|
||||
vecElems[index] = Src(VariableType.S32);
|
||||
|
||||
if (type == VariableType.F32)
|
||||
{
|
||||
vecElems[index] = "float(" + vecElems[index] + ")";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vecElems[index] = Src(type);
|
||||
}
|
||||
}
|
||||
|
||||
string prefix = type == VariableType.F32 ? string.Empty : "i";
|
||||
|
||||
return prefix + "vec" + count + "(" + string.Join(", ", vecElems) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return Src(type);
|
||||
}
|
||||
}
|
||||
|
||||
Append(AssembleVector(pCount, intCoords ? VariableType.S32 : VariableType.F32));
|
||||
|
||||
if (hasExtraCompareArg)
|
||||
{
|
||||
Append(Src(VariableType.F32));
|
||||
}
|
||||
|
||||
if (isMultisample)
|
||||
{
|
||||
Append(Src(VariableType.S32));
|
||||
}
|
||||
|
||||
if (hasLodLevel)
|
||||
{
|
||||
Append(Src(intCoords ? VariableType.S32 : VariableType.F32));
|
||||
}
|
||||
|
||||
if (hasOffset)
|
||||
{
|
||||
Append(AssembleVector(coordsCount, VariableType.S32));
|
||||
}
|
||||
else if (hasOffsets)
|
||||
{
|
||||
const int gatherTexelsCount = 4;
|
||||
|
||||
texCall += $", ivec{coordsCount}[{gatherTexelsCount}](";
|
||||
|
||||
for (int index = 0; index < gatherTexelsCount; index++)
|
||||
{
|
||||
texCall += AssembleVector(coordsCount, VariableType.S32);
|
||||
|
||||
if (index < gatherTexelsCount - 1)
|
||||
{
|
||||
texCall += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
texCall += ")";
|
||||
}
|
||||
|
||||
if (hasLodBias)
|
||||
{
|
||||
Append(Src(VariableType.F32));
|
||||
}
|
||||
|
||||
//textureGather* optional extra component index,
|
||||
//not needed for shadow samplers.
|
||||
if (isGather && !isShadow)
|
||||
{
|
||||
Append(Src(VariableType.S32));
|
||||
}
|
||||
|
||||
texCall += ")" + (isGather || !isShadow ? GetMask(texOp.ComponentMask) : "");
|
||||
|
||||
return texCall;
|
||||
}
|
||||
|
||||
public static string TextureSize(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
string samplerName = OperandManager.GetSamplerName(context.ShaderType, texOp);
|
||||
|
||||
IAstNode src0 = operation.GetSource(isBindless ? 1 : 0);
|
||||
|
||||
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
|
||||
|
||||
return $"textureSize({samplerName}, {src0Expr}){GetMask(texOp.ComponentMask)}";
|
||||
}
|
||||
|
||||
private static string GetMask(int compMask)
|
||||
{
|
||||
string mask = ".";
|
||||
|
||||
for (int index = 0; index < 4; index++)
|
||||
{
|
||||
if ((compMask & (1 << index)) != 0)
|
||||
{
|
||||
mask += "rgba".Substring(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
static class InstGenPacking
|
||||
{
|
||||
public static string PackHalf2x16(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src0 = operation.GetSource(0);
|
||||
IAstNode src1 = operation.GetSource(1);
|
||||
|
||||
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
|
||||
string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
return $"packHalf2x16(vec2({src0Expr}, {src1Expr}))";
|
||||
}
|
||||
|
||||
public static string UnpackHalf2x16(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src = operation.GetSource(0);
|
||||
|
||||
string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
|
||||
|
||||
return $"unpackHalf2x16({srcExpr}){GetMask(operation.ComponentMask)}";
|
||||
}
|
||||
|
||||
private static string GetMask(int compMask)
|
||||
{
|
||||
string mask = ".";
|
||||
|
||||
for (int index = 0; index < 2; index++)
|
||||
{
|
||||
if ((compMask & (1 << index)) != 0)
|
||||
{
|
||||
mask += "xy".Substring(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
struct InstInfo
|
||||
{
|
||||
public InstType Type { get; }
|
||||
|
||||
public string OpName { get; }
|
||||
|
||||
public int Precedence { get; }
|
||||
|
||||
public InstInfo(InstType type, string opName, int precedence)
|
||||
{
|
||||
Type = type;
|
||||
OpName = opName;
|
||||
Precedence = precedence;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
[Flags]
|
||||
enum InstType
|
||||
{
|
||||
OpNullary = Op | 0,
|
||||
OpUnary = Op | 1,
|
||||
OpBinary = Op | 2,
|
||||
OpTernary = Op | 3,
|
||||
OpBinaryCom = OpBinary | Comutative,
|
||||
|
||||
CallNullary = Call | 0,
|
||||
CallUnary = Call | 1,
|
||||
CallBinary = Call | 2,
|
||||
CallTernary = Call | 3,
|
||||
CallQuaternary = Call | 4,
|
||||
|
||||
Comutative = 1 << 8,
|
||||
Op = 1 << 9,
|
||||
Call = 1 << 10,
|
||||
Special = 1 << 11,
|
||||
|
||||
ArityMask = 0xff
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
case AttributeConsts.PositionX: return "gl_FragCoord.x";
|
||||
case AttributeConsts.PositionY: return "gl_FragCoord.y";
|
||||
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
||||
case AttributeConsts.PositionW: return "1";
|
||||
case AttributeConsts.PositionW: return "1.0";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,9 +145,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return ubName + "_" + DefaultNames.UniformNameSuffix;
|
||||
}
|
||||
|
||||
public static string GetSamplerName(GalShaderType shaderType, int textureHandle)
|
||||
public static string GetSamplerName(GalShaderType shaderType, AstTextureOperation texOp)
|
||||
{
|
||||
string suffix = (textureHandle - 8).ToString();
|
||||
string suffix;
|
||||
|
||||
if ((texOp.Flags & TextureFlags.Bindless) != 0)
|
||||
{
|
||||
AstOperand operand = texOp.GetSource(0) as AstOperand;
|
||||
|
||||
suffix = "_cb" + operand.CbufSlot + "_" + operand.CbufOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix = (texOp.Handle - 8).ToString();
|
||||
}
|
||||
|
||||
return GetShaderStagePrefix(shaderType) + "_" + DefaultNames.SamplerNamePrefix + suffix;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
|
@ -20,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
}
|
||||
|
||||
string expr = Instructions.GetExpression(context, node);
|
||||
string expr = InstGen.GetExpression(context, node);
|
||||
|
||||
return ReinterpretCast(expr, node, srcType, dstType);
|
||||
}
|
||||
|
@ -55,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
else if (dstType == VariableType.Bool)
|
||||
{
|
||||
expr = Instructions.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true);
|
||||
expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true);
|
||||
|
||||
return $"({expr} != 0)";
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType);
|
||||
string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType);
|
||||
|
||||
expr = Instructions.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false);
|
||||
expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false);
|
||||
|
||||
return $"({expr} ? {trueExpr} : {falseExpr})";
|
||||
}
|
||||
|
|
|
@ -130,8 +130,15 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy));
|
||||
Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync));
|
||||
Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
|
||||
Set("1101111010111x", InstEmit.Tex_B, typeof(OpCodeTex));
|
||||
Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs));
|
||||
Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTex));
|
||||
Set("1101101xxxxxxx", InstEmit.Texs, typeof(OpCodeTlds));
|
||||
Set("1101111100xxxx", InstEmit.Texs, typeof(OpCodeTld4s));
|
||||
Set("1101110000111x", InstEmit.Tld, typeof(OpCodeTld));
|
||||
Set("1101110100111x", InstEmit.Tld_B, typeof(OpCodeTld));
|
||||
Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4));
|
||||
Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex));
|
||||
Set("1101111101010x", InstEmit.Txq_B, typeof(OpCodeTex));
|
||||
Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf));
|
||||
Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm));
|
||||
Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf));
|
||||
|
|
|
@ -2,45 +2,13 @@ using Ryujinx.Graphics.Shader.Instructions;
|
|||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTex : OpCode
|
||||
class OpCodeTex : OpCodeTexture
|
||||
{
|
||||
public Register Rd { get; }
|
||||
public Register Ra { get; }
|
||||
public Register Rb { get; }
|
||||
|
||||
public bool IsArray { get; }
|
||||
|
||||
public TextureDimensions Dimensions { get; }
|
||||
|
||||
public int ComponentMask { get; }
|
||||
|
||||
public int Immediate { get; }
|
||||
|
||||
public bool HasDepthCompare { get; }
|
||||
|
||||
public bool HasOffset { get; }
|
||||
|
||||
public TextureLodMode LodMode { get; }
|
||||
|
||||
public OpCodeTex(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
|
||||
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
|
||||
Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
|
||||
|
||||
IsArray = opCode.Extract(28);
|
||||
|
||||
Dimensions = (TextureDimensions)opCode.Extract(29, 2);
|
||||
|
||||
ComponentMask = opCode.Extract(31, 4);
|
||||
|
||||
Immediate = opCode.Extract(36, 13);
|
||||
|
||||
HasDepthCompare = opCode.Extract(50);
|
||||
|
||||
HasOffset = opCode.Extract(54);
|
||||
|
||||
LodMode = (TextureLodMode)opCode.Extract(55, 3);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,67 +2,10 @@ using Ryujinx.Graphics.Shader.Instructions;
|
|||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTexs : OpCode
|
||||
class OpCodeTexs : OpCodeTextureScalar
|
||||
{
|
||||
#region "Component mask LUT"
|
||||
private const int ____ = 0x0;
|
||||
private const int R___ = 0x1;
|
||||
private const int _G__ = 0x2;
|
||||
private const int RG__ = 0x3;
|
||||
private const int __B_ = 0x4;
|
||||
private const int RGB_ = 0x7;
|
||||
private const int ___A = 0x8;
|
||||
private const int R__A = 0x9;
|
||||
private const int _G_A = 0xa;
|
||||
private const int RG_A = 0xb;
|
||||
private const int __BA = 0xc;
|
||||
private const int R_BA = 0xd;
|
||||
private const int _GBA = 0xe;
|
||||
private const int RGBA = 0xf;
|
||||
public TexsType Type => (TexsType)RawType;
|
||||
|
||||
private static int[,] _maskLut = new int[,]
|
||||
{
|
||||
{ ____, ____, ____, ____, ____, ____, ____, ____ },
|
||||
{ R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA },
|
||||
{ R___, _G__, __B_, ___A, RG__, ____, ____, ____ },
|
||||
{ RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
|
||||
};
|
||||
#endregion
|
||||
|
||||
public Register Rd0 { get; }
|
||||
public Register Ra { get; }
|
||||
public Register Rb { get; }
|
||||
public Register Rd1 { get; }
|
||||
|
||||
public int Immediate { get; }
|
||||
|
||||
public int ComponentMask { get; }
|
||||
|
||||
public TexsType Type { get; }
|
||||
|
||||
public bool IsFp16 { get; }
|
||||
|
||||
public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
Rd0 = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
|
||||
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
|
||||
Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
|
||||
Rd1 = new Register(opCode.Extract(28, 8), RegisterType.Gpr);
|
||||
|
||||
Immediate = opCode.Extract(36, 13);
|
||||
|
||||
int compSel = opCode.Extract(50, 3);
|
||||
|
||||
Type = (TexsType)opCode.Extract(53, 4);
|
||||
|
||||
IsFp16 = !opCode.Extract(59);
|
||||
|
||||
int rdMask;
|
||||
|
||||
rdMask = Rd0.IsRZ ? 0 : 1;
|
||||
rdMask |= Rd1.IsRZ ? 0 : 2;
|
||||
|
||||
ComponentMask = _maskLut[rdMask, compSel];
|
||||
}
|
||||
public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { }
|
||||
}
|
||||
}
|
42
Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs
Normal file
42
Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTexture : OpCode
|
||||
{
|
||||
public Register Rd { get; }
|
||||
public Register Ra { get; }
|
||||
public Register Rb { get; }
|
||||
|
||||
public bool IsArray { get; }
|
||||
|
||||
public TextureDimensions Dimensions { get; }
|
||||
|
||||
public int ComponentMask { get; }
|
||||
|
||||
public int Immediate { get; }
|
||||
|
||||
public bool HasOffset { get; }
|
||||
|
||||
public TextureLodMode LodMode { get; protected set; }
|
||||
|
||||
public OpCodeTexture(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
|
||||
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
|
||||
Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
|
||||
|
||||
IsArray = opCode.Extract(28);
|
||||
|
||||
Dimensions = (TextureDimensions)opCode.Extract(29, 2);
|
||||
|
||||
ComponentMask = opCode.Extract(31, 4);
|
||||
|
||||
Immediate = opCode.Extract(36, 13);
|
||||
|
||||
HasOffset = opCode.Extract(54);
|
||||
|
||||
LodMode = (TextureLodMode)opCode.Extract(55, 3);
|
||||
}
|
||||
}
|
||||
}
|
61
Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs
Normal file
61
Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTextureScalar : OpCode
|
||||
{
|
||||
#region "Component mask LUT"
|
||||
private const int ____ = 0x0;
|
||||
private const int R___ = 0x1;
|
||||
private const int _G__ = 0x2;
|
||||
private const int RG__ = 0x3;
|
||||
private const int __B_ = 0x4;
|
||||
private const int RGB_ = 0x7;
|
||||
private const int ___A = 0x8;
|
||||
private const int R__A = 0x9;
|
||||
private const int _G_A = 0xa;
|
||||
private const int RG_A = 0xb;
|
||||
private const int __BA = 0xc;
|
||||
private const int R_BA = 0xd;
|
||||
private const int _GBA = 0xe;
|
||||
private const int RGBA = 0xf;
|
||||
|
||||
private static int[,] _maskLut = new int[,]
|
||||
{
|
||||
{ R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA },
|
||||
{ RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
|
||||
};
|
||||
#endregion
|
||||
|
||||
public Register Rd0 { get; }
|
||||
public Register Ra { get; }
|
||||
public Register Rb { get; }
|
||||
public Register Rd1 { get; }
|
||||
|
||||
public int Immediate { get; }
|
||||
|
||||
public int ComponentMask { get; }
|
||||
|
||||
protected int RawType;
|
||||
|
||||
public bool IsFp16 { get; }
|
||||
|
||||
public OpCodeTextureScalar(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
Rd0 = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
|
||||
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
|
||||
Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
|
||||
Rd1 = new Register(opCode.Extract(28, 8), RegisterType.Gpr);
|
||||
|
||||
Immediate = opCode.Extract(36, 13);
|
||||
|
||||
int compSel = opCode.Extract(50, 3);
|
||||
|
||||
RawType = opCode.Extract(53, 4);
|
||||
|
||||
IsFp16 = !opCode.Extract(59);
|
||||
|
||||
ComponentMask = _maskLut[Rd1.IsRZ ? 0 : 1, compSel];
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs
Normal file
16
Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTld : OpCodeTexture
|
||||
{
|
||||
public bool IsMultisample { get; }
|
||||
|
||||
public OpCodeTld(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
IsMultisample = opCode.Extract(50);
|
||||
|
||||
LodMode = (TextureLodMode)(opCode.Extract(55, 1) + TextureLodMode.LodZero);
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs
Normal file
22
Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTld4 : OpCodeTexture
|
||||
{
|
||||
public bool HasDepthCompare { get; }
|
||||
|
||||
public TextureGatherOffset Offset { get; }
|
||||
|
||||
public int GatherCompIndex { get; }
|
||||
|
||||
public OpCodeTld4(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
HasDepthCompare = opCode.Extract(50);
|
||||
|
||||
Offset = (TextureGatherOffset)opCode.Extract(54, 2);
|
||||
|
||||
GatherCompIndex = opCode.Extract(56, 2);
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs
Normal file
20
Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTld4s : OpCodeTextureScalar
|
||||
{
|
||||
public bool HasDepthCompare { get; }
|
||||
public bool HasOffset { get; }
|
||||
|
||||
public int GatherCompIndex { get; }
|
||||
|
||||
public OpCodeTld4s(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||
{
|
||||
HasDepthCompare = opCode.Extract(50);
|
||||
HasOffset = opCode.Extract(51);
|
||||
|
||||
GatherCompIndex = opCode.Extract(52, 2);
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs
Normal file
11
Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
class OpCodeTlds : OpCodeTextureScalar
|
||||
{
|
||||
public TldsType Type => (TldsType)RawType;
|
||||
|
||||
public OpCodeTlds(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { }
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics/Shader/Decoders/TldsType.cs
Normal file
15
Ryujinx.Graphics/Shader/Decoders/TldsType.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
enum TldsType
|
||||
{
|
||||
Texture1DLodZero = 0x0,
|
||||
Texture1DLodLevel = 0x1,
|
||||
Texture2DLodZero = 0x2,
|
||||
Texture2DLodZeroOffset = 0x4,
|
||||
Texture2DLodLevel = 0x5,
|
||||
Texture2DLodZeroMultisample = 0x6,
|
||||
Texture3DLodZero = 0x7,
|
||||
Texture2DArrayLodZero = 0x8,
|
||||
Texture2DLodLevelOffset = 0xc
|
||||
}
|
||||
}
|
|
@ -139,13 +139,35 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
public static void Tex(EmitterContext context)
|
||||
{
|
||||
OpCodeTex op = (OpCodeTex)context.CurrOp;
|
||||
Tex(context, TextureFlags.None);
|
||||
}
|
||||
|
||||
if (op.Rd.IsRZ)
|
||||
public static void Tex_B(EmitterContext context)
|
||||
{
|
||||
Tex(context, TextureFlags.Bindless);
|
||||
}
|
||||
|
||||
public static void Tld(EmitterContext context)
|
||||
{
|
||||
Tex(context, TextureFlags.IntCoords);
|
||||
}
|
||||
|
||||
public static void Tld_B(EmitterContext context)
|
||||
{
|
||||
Tex(context, TextureFlags.IntCoords | TextureFlags.Bindless);
|
||||
}
|
||||
|
||||
public static void Texs(EmitterContext context)
|
||||
{
|
||||
OpCodeTextureScalar op = (OpCodeTextureScalar)context.CurrOp;
|
||||
|
||||
if (op.Rd0.IsRZ && op.Rd1.IsRZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
int raIndex = op.Ra.Index;
|
||||
int rbIndex = op.Rb.Index;
|
||||
|
||||
|
@ -169,200 +191,157 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
Operand arrayIndex = op.IsArray ? Ra() : null;
|
||||
TextureType type;
|
||||
TextureFlags flags;
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
TextureType type = GetTextureType(op.Dimensions);
|
||||
|
||||
TextureFlags flags = TextureFlags.None;
|
||||
|
||||
int elemsCount = GetTextureCoordsCount(type);
|
||||
|
||||
for (int index = 0; index < elemsCount; index++)
|
||||
if (op is OpCodeTexs texsOp)
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
type = GetTextureType (texsOp.Type);
|
||||
flags = GetTextureFlags(texsOp.Type);
|
||||
|
||||
if (op.IsArray)
|
||||
{
|
||||
sourcesList.Add(arrayIndex);
|
||||
|
||||
type |= TextureType.Array;
|
||||
}
|
||||
|
||||
bool hasLod = op.LodMode > TextureLodMode.LodZero;
|
||||
|
||||
Operand lodValue = hasLod ? Rb() : ConstF(0);
|
||||
|
||||
Operand packedOffs = op.HasOffset ? Rb() : null;
|
||||
|
||||
if (op.HasDepthCompare)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
if (op.LodMode == TextureLodMode.LodZero ||
|
||||
op.LodMode == TextureLodMode.LodLevel ||
|
||||
op.LodMode == TextureLodMode.LodLevelA)
|
||||
{
|
||||
sourcesList.Add(lodValue);
|
||||
|
||||
flags |= TextureFlags.LodLevel;
|
||||
}
|
||||
|
||||
if (op.HasOffset)
|
||||
{
|
||||
for (int index = 0; index < elemsCount; index++)
|
||||
if ((type & TextureType.Array) != 0)
|
||||
{
|
||||
sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
|
||||
Operand arrayIndex = Ra();
|
||||
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
|
||||
sourcesList.Add(arrayIndex);
|
||||
|
||||
if ((type & TextureType.Shadow) != 0)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
if ((flags & TextureFlags.LodLevel) != 0)
|
||||
{
|
||||
sourcesList.Add(ConstF(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (texsOp.Type)
|
||||
{
|
||||
case TexsType.Texture1DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
break;
|
||||
|
||||
case TexsType.Texture2D:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodLevel:
|
||||
case TexsType.Texture2DDepthCompare:
|
||||
case TexsType.Texture3D:
|
||||
case TexsType.TextureCube:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodZeroDepthCompare:
|
||||
case TexsType.Texture3DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodLevelDepthCompare:
|
||||
case TexsType.TextureCubeLodLevel:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (op is OpCodeTlds tldsOp)
|
||||
{
|
||||
type = GetTextureType (tldsOp.Type);
|
||||
flags = GetTextureFlags(tldsOp.Type) | TextureFlags.IntCoords;
|
||||
|
||||
switch (tldsOp.Type)
|
||||
{
|
||||
case TldsType.Texture1DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TldsType.Texture1DLodLevel:
|
||||
case TldsType.Texture2DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
|
||||
case TldsType.Texture2DLodZeroOffset:
|
||||
case TldsType.Texture2DLodLevel:
|
||||
case TldsType.Texture2DLodZeroMultisample:
|
||||
case TldsType.Texture3DLodZero:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
|
||||
case TldsType.Texture2DArrayLodZero:
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(Ra());
|
||||
break;
|
||||
|
||||
case TldsType.Texture2DLodLevelOffset:
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
sourcesList.Add(Rb());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (op is OpCodeTld4s tld4sOp)
|
||||
{
|
||||
if (!(tld4sOp.HasDepthCompare || tld4sOp.HasOffset))
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
|
||||
flags |= TextureFlags.Offset;
|
||||
type = TextureType.Texture2D;
|
||||
flags = TextureFlags.Gather;
|
||||
|
||||
if (tld4sOp.HasOffset)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
|
||||
flags |= TextureFlags.Offset;
|
||||
}
|
||||
|
||||
if (tld4sOp.HasDepthCompare)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
|
||||
type |= TextureType.Shadow;
|
||||
}
|
||||
}
|
||||
|
||||
if (op.LodMode == TextureLodMode.LodBias ||
|
||||
op.LodMode == TextureLodMode.LodBiasA)
|
||||
else
|
||||
{
|
||||
sourcesList.Add(lodValue);
|
||||
|
||||
flags |= TextureFlags.LodBias;
|
||||
throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\".");
|
||||
}
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
int rdIndex = op.Rd.Index;
|
||||
|
||||
Operand GetDest()
|
||||
{
|
||||
if (rdIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return Register(rdIndex++, RegisterType.Gpr);
|
||||
}
|
||||
|
||||
int textureHandle = op.Immediate;
|
||||
|
||||
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
if ((compMask & 1) != 0)
|
||||
{
|
||||
Operand dest = GetDest();
|
||||
|
||||
TextureOperation operation = new TextureOperation(
|
||||
Instruction.TextureSample,
|
||||
type,
|
||||
flags,
|
||||
textureHandle,
|
||||
compIndex,
|
||||
dest,
|
||||
sources);
|
||||
|
||||
context.Add(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Texs(EmitterContext context)
|
||||
{
|
||||
OpCodeTexs op = (OpCodeTexs)context.CurrOp;
|
||||
|
||||
if (op.Rd0.IsRZ && op.Rd1.IsRZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
Operand Src(int index)
|
||||
{
|
||||
int regIndex = 0;
|
||||
|
||||
switch (index & 2)
|
||||
{
|
||||
case 0: regIndex = op.Ra.Index; break;
|
||||
case 2: regIndex = op.Rb.Index; break;
|
||||
}
|
||||
|
||||
if (regIndex != RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
regIndex += index & 1;
|
||||
}
|
||||
|
||||
return context.Copy(Register(regIndex, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
switch (op.Type)
|
||||
{
|
||||
case TexsType.Texture1DLodZero:
|
||||
sourcesList.Add(Src(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2D:
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(2));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodZero:
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodLevel:
|
||||
case TexsType.Texture2DDepthCompare:
|
||||
case TexsType.Texture3D:
|
||||
case TexsType.TextureCube:
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodZeroDepthCompare:
|
||||
case TexsType.Texture3DLodZero:
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DLodLevelDepthCompare:
|
||||
case TexsType.TextureCubeLodLevel:
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(Src(3));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DArray:
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(Src(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DArrayLodZero:
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
|
||||
case TexsType.Texture2DArrayLodZeroDepthCompare:
|
||||
sourcesList.Add(Src(1));
|
||||
sourcesList.Add(Src(2));
|
||||
sourcesList.Add(Src(0));
|
||||
sourcesList.Add(Src(3));
|
||||
sourcesList.Add(ConstF(0));
|
||||
break;
|
||||
}
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
TextureType type = GetTextureType (op.Type);
|
||||
TextureFlags flags = GetTextureFlags(op.Type);
|
||||
|
||||
Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
|
||||
Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
|
||||
|
||||
|
@ -394,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
}
|
||||
}
|
||||
|
||||
int textureHandle = op.Immediate;
|
||||
int handle = op.Immediate;
|
||||
|
||||
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
|
@ -406,7 +385,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
Instruction.TextureSample,
|
||||
type,
|
||||
flags,
|
||||
textureHandle,
|
||||
handle,
|
||||
compIndex,
|
||||
dest,
|
||||
sources);
|
||||
|
@ -424,17 +403,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
public static void Tld4(EmitterContext context)
|
||||
{
|
||||
OpCodeTex op = (OpCodeTex)context.CurrOp;
|
||||
OpCodeTld4 op = (OpCodeTld4)context.CurrOp;
|
||||
|
||||
if (op.Rd.IsRZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TextureGatherOffset offset = (TextureGatherOffset)op.RawOpCode.Extract(54, 2);
|
||||
|
||||
int gatherCompIndex = op.RawOpCode.Extract(56, 2);
|
||||
|
||||
int raIndex = op.Ra.Index;
|
||||
int rbIndex = op.Rb.Index;
|
||||
|
||||
|
@ -466,9 +441,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
TextureFlags flags = TextureFlags.Gather;
|
||||
|
||||
int elemsCount = GetTextureCoordsCount(type);
|
||||
int coordsCount = type.GetCoordsCount();
|
||||
|
||||
for (int index = 0; index < elemsCount; index++)
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
|
@ -482,31 +457,31 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
Operand[] packedOffs = new Operand[2];
|
||||
|
||||
packedOffs[0] = offset != TextureGatherOffset.None ? Rb() : null;
|
||||
packedOffs[1] = offset == TextureGatherOffset.Offsets ? Rb() : null;
|
||||
packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null;
|
||||
packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null;
|
||||
|
||||
if (op.HasDepthCompare)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
if (offset != TextureGatherOffset.None)
|
||||
if (op.Offset != TextureGatherOffset.None)
|
||||
{
|
||||
int offsetTexelsCount = offset == TextureGatherOffset.Offsets ? 4 : 1;
|
||||
int offsetTexelsCount = op.Offset == TextureGatherOffset.Offsets ? 4 : 1;
|
||||
|
||||
for (int index = 0; index < elemsCount * offsetTexelsCount; index++)
|
||||
for (int index = 0; index < coordsCount * offsetTexelsCount; index++)
|
||||
{
|
||||
Operand packed = packedOffs[(index >> 2) & 1];
|
||||
|
||||
sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6)));
|
||||
}
|
||||
|
||||
flags |= offset == TextureGatherOffset.Offsets
|
||||
flags |= op.Offset == TextureGatherOffset.Offsets
|
||||
? TextureFlags.Offsets
|
||||
: TextureFlags.Offset;
|
||||
}
|
||||
|
||||
sourcesList.Add(Const(gatherCompIndex));
|
||||
sourcesList.Add(Const(op.GatherCompIndex));
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
|
@ -522,7 +497,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
return Register(rdIndex++, RegisterType.Gpr);
|
||||
}
|
||||
|
||||
int textureHandle = op.Immediate;
|
||||
int handle = op.Immediate;
|
||||
|
||||
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
|
@ -534,7 +509,231 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
Instruction.TextureSample,
|
||||
type,
|
||||
flags,
|
||||
textureHandle,
|
||||
handle,
|
||||
compIndex,
|
||||
dest,
|
||||
sources);
|
||||
|
||||
context.Add(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Txq(EmitterContext context)
|
||||
{
|
||||
Txq(context, bindless: false);
|
||||
}
|
||||
|
||||
public static void Txq_B(EmitterContext context)
|
||||
{
|
||||
Txq(context, bindless: true);
|
||||
}
|
||||
|
||||
private static void Txq(EmitterContext context, bool bindless)
|
||||
{
|
||||
OpCodeTex op = (OpCodeTex)context.CurrOp;
|
||||
|
||||
if (op.Rd.IsRZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6);
|
||||
|
||||
//TODO: Validate and use property.
|
||||
Instruction inst = Instruction.TextureSize;
|
||||
|
||||
TextureType type = TextureType.Texture2D;
|
||||
|
||||
TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||
|
||||
int raIndex = op.Ra.Index;
|
||||
|
||||
Operand Ra()
|
||||
{
|
||||
if (raIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return context.Copy(Register(raIndex++, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
if (bindless)
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
|
||||
sourcesList.Add(Ra());
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
int rdIndex = op.Rd.Index;
|
||||
|
||||
Operand GetDest()
|
||||
{
|
||||
if (rdIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return Register(rdIndex++, RegisterType.Gpr);
|
||||
}
|
||||
|
||||
int handle = !bindless ? op.Immediate : 0;
|
||||
|
||||
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
if ((compMask & 1) != 0)
|
||||
{
|
||||
Operand dest = GetDest();
|
||||
|
||||
TextureOperation operation = new TextureOperation(
|
||||
inst,
|
||||
type,
|
||||
flags,
|
||||
handle,
|
||||
compIndex,
|
||||
dest,
|
||||
sources);
|
||||
|
||||
context.Add(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Tex(EmitterContext context, TextureFlags flags)
|
||||
{
|
||||
OpCodeTexture op = (OpCodeTexture)context.CurrOp;
|
||||
|
||||
bool isBindless = (flags & TextureFlags.Bindless) != 0;
|
||||
bool intCoords = (flags & TextureFlags.IntCoords) != 0;
|
||||
|
||||
if (op.Rd.IsRZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int raIndex = op.Ra.Index;
|
||||
int rbIndex = op.Rb.Index;
|
||||
|
||||
Operand Ra()
|
||||
{
|
||||
if (raIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return context.Copy(Register(raIndex++, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
Operand Rb()
|
||||
{
|
||||
if (rbIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
Operand arrayIndex = op.IsArray ? Ra() : null;
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
if (isBindless)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
TextureType type = GetTextureType(op.Dimensions);
|
||||
|
||||
int coordsCount = type.GetCoordsCount();
|
||||
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
|
||||
if (op.IsArray)
|
||||
{
|
||||
sourcesList.Add(arrayIndex);
|
||||
|
||||
type |= TextureType.Array;
|
||||
}
|
||||
|
||||
bool hasLod = op.LodMode > TextureLodMode.LodZero;
|
||||
|
||||
Operand lodValue = hasLod ? Rb() : ConstF(0);
|
||||
|
||||
Operand packedOffs = op.HasOffset ? Rb() : null;
|
||||
|
||||
if (op is OpCodeTex texOp && texOp.HasDepthCompare)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
if (op.LodMode == TextureLodMode.LodZero ||
|
||||
op.LodMode == TextureLodMode.LodLevel ||
|
||||
op.LodMode == TextureLodMode.LodLevelA)
|
||||
{
|
||||
sourcesList.Add(lodValue);
|
||||
|
||||
flags |= TextureFlags.LodLevel;
|
||||
}
|
||||
|
||||
if (op.HasOffset)
|
||||
{
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
|
||||
}
|
||||
|
||||
flags |= TextureFlags.Offset;
|
||||
}
|
||||
|
||||
if (op.LodMode == TextureLodMode.LodBias ||
|
||||
op.LodMode == TextureLodMode.LodBiasA)
|
||||
{
|
||||
sourcesList.Add(lodValue);
|
||||
|
||||
flags |= TextureFlags.LodBias;
|
||||
}
|
||||
|
||||
if (op is OpCodeTld tldOp && tldOp.IsMultisample)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
int rdIndex = op.Rd.Index;
|
||||
|
||||
Operand GetDest()
|
||||
{
|
||||
if (rdIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return Register(rdIndex++, RegisterType.Gpr);
|
||||
}
|
||||
|
||||
int handle = !isBindless ? op.Immediate : 0;
|
||||
|
||||
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
if ((compMask & 1) != 0)
|
||||
{
|
||||
Operand dest = GetDest();
|
||||
|
||||
TextureOperation operation = new TextureOperation(
|
||||
Instruction.TextureSample,
|
||||
type,
|
||||
flags,
|
||||
handle,
|
||||
compIndex,
|
||||
dest,
|
||||
sources);
|
||||
|
@ -557,19 +756,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".");
|
||||
}
|
||||
|
||||
private static int GetTextureCoordsCount(TextureType type)
|
||||
{
|
||||
switch (type & TextureType.Mask)
|
||||
{
|
||||
case TextureType.Texture1D: return 1;
|
||||
case TextureType.Texture2D: return 2;
|
||||
case TextureType.Texture3D: return 3;
|
||||
case TextureType.TextureCube: return 3;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
|
||||
private static TextureType GetTextureType(TexsType type)
|
||||
{
|
||||
switch (type)
|
||||
|
@ -606,6 +792,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
|
||||
private static TextureType GetTextureType(TldsType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TldsType.Texture1DLodZero:
|
||||
case TldsType.Texture1DLodLevel:
|
||||
return TextureType.Texture1D;
|
||||
|
||||
case TldsType.Texture2DLodZero:
|
||||
case TldsType.Texture2DLodZeroOffset:
|
||||
case TldsType.Texture2DLodLevel:
|
||||
case TldsType.Texture2DLodLevelOffset:
|
||||
return TextureType.Texture2D;
|
||||
|
||||
case TldsType.Texture2DLodZeroMultisample:
|
||||
return TextureType.Texture2D | TextureType.Multisample;
|
||||
|
||||
case TldsType.Texture3DLodZero:
|
||||
return TextureType.Texture3D;
|
||||
|
||||
case TldsType.Texture2DArrayLodZero:
|
||||
return TextureType.Texture2D | TextureType.Array;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
|
||||
private static TextureFlags GetTextureFlags(TexsType type)
|
||||
{
|
||||
switch (type)
|
||||
|
@ -631,5 +844,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
|
||||
private static TextureFlags GetTextureFlags(TldsType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TldsType.Texture1DLodZero:
|
||||
case TldsType.Texture1DLodLevel:
|
||||
case TldsType.Texture2DLodZero:
|
||||
case TldsType.Texture2DLodLevel:
|
||||
case TldsType.Texture2DLodZeroMultisample:
|
||||
case TldsType.Texture3DLodZero:
|
||||
case TldsType.Texture2DArrayLodZero:
|
||||
return TextureFlags.LodLevel;
|
||||
|
||||
case TldsType.Texture2DLodZeroOffset:
|
||||
case TldsType.Texture2DLodLevelOffset:
|
||||
return TextureFlags.LodLevel | TextureFlags.Offset;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics/Shader/Instructions/TextureProperty.cs
Normal file
13
Ryujinx.Graphics/Shader/Instructions/TextureProperty.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ryujinx.Graphics.Shader.Instructions
|
||||
{
|
||||
enum TextureProperty
|
||||
{
|
||||
Dimensions = 0x1,
|
||||
Type = 0x2,
|
||||
SamplePos = 0x5,
|
||||
Filter = 0xa,
|
||||
Lod = 0xc,
|
||||
Wrap = 0xe,
|
||||
BorderColor = 0x10
|
||||
}
|
||||
}
|
|
@ -75,11 +75,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
StoreLocal,
|
||||
Subtract,
|
||||
TextureSample,
|
||||
TextureSize,
|
||||
Truncate,
|
||||
UnpackDouble2x32High,
|
||||
UnpackDouble2x32Low,
|
||||
UnpackHalf2x16High,
|
||||
UnpackHalf2x16Low,
|
||||
UnpackDouble2x32,
|
||||
UnpackHalf2x16,
|
||||
|
||||
Count,
|
||||
FP = 1 << 16,
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
|
||||
public int SourcesCount => _sources.Length;
|
||||
|
||||
public int ComponentIndex { get; }
|
||||
|
||||
public Operation(Instruction inst, Operand dest, params Operand[] sources)
|
||||
{
|
||||
Inst = inst;
|
||||
|
@ -33,6 +35,15 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
}
|
||||
}
|
||||
|
||||
public Operation(
|
||||
Instruction inst,
|
||||
int compIndex,
|
||||
Operand dest,
|
||||
params Operand[] sources) : this(inst, dest, sources)
|
||||
{
|
||||
ComponentIndex = compIndex;
|
||||
}
|
||||
|
||||
private Operand AssignDest(Operand dest)
|
||||
{
|
||||
if (dest != null && dest.Type == OperandType.LocalVariable)
|
||||
|
|
|
@ -5,11 +5,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
[Flags]
|
||||
enum TextureFlags
|
||||
{
|
||||
None = 0,
|
||||
Gather = 1 << 0,
|
||||
LodBias = 1 << 1,
|
||||
LodLevel = 1 << 2,
|
||||
Offset = 1 << 3,
|
||||
Offsets = 1 << 4
|
||||
None = 0,
|
||||
Bindless = 1 << 0,
|
||||
Gather = 1 << 1,
|
||||
IntCoords = 1 << 2,
|
||||
LodBias = 1 << 3,
|
||||
LodLevel = 1 << 4,
|
||||
Offset = 1 << 5,
|
||||
Offsets = 1 << 6
|
||||
}
|
||||
}
|
|
@ -5,22 +5,20 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
public TextureType Type { get; }
|
||||
public TextureFlags Flags { get; }
|
||||
|
||||
public int TextureHandle { get; }
|
||||
public int ComponentIndex { get; }
|
||||
public int Handle { get; }
|
||||
|
||||
public TextureOperation(
|
||||
Instruction inst,
|
||||
TextureType type,
|
||||
TextureFlags flags,
|
||||
int textureHandle,
|
||||
int componentIndex,
|
||||
int handle,
|
||||
int compIndex,
|
||||
Operand dest,
|
||||
params Operand[] sources) : base(inst, dest, sources)
|
||||
params Operand[] sources) : base(inst, compIndex, dest, sources)
|
||||
{
|
||||
Type = type;
|
||||
Flags = flags;
|
||||
TextureHandle = textureHandle;
|
||||
ComponentIndex = componentIndex;
|
||||
Type = type;
|
||||
Flags = flags;
|
||||
Handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,24 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
|
||||
Mask = 0xff,
|
||||
|
||||
Array = 1 << 8,
|
||||
Shadow = 1 << 9
|
||||
Array = 1 << 8,
|
||||
Multisample = 1 << 9,
|
||||
Shadow = 1 << 10
|
||||
}
|
||||
|
||||
static class TextureTypeExtensions
|
||||
{
|
||||
public static int GetCoordsCount(this TextureType type)
|
||||
{
|
||||
switch (type & TextureType.Mask)
|
||||
{
|
||||
case TextureType.Texture1D: return 1;
|
||||
case TextureType.Texture2D: return 2;
|
||||
case TextureType.Texture3D: return 3;
|
||||
case TextureType.TextureCube: return 3;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
public Instruction Inst { get; }
|
||||
|
||||
public int ComponentMask { get; }
|
||||
|
||||
private IAstNode[] _sources;
|
||||
|
||||
public int SourcesCount => _sources.Length;
|
||||
|
@ -21,6 +23,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
AddUse(source, this);
|
||||
}
|
||||
|
||||
ComponentMask = 1;
|
||||
}
|
||||
|
||||
public AstOperation(Instruction inst, int compMask, params IAstNode[] sources) : this(inst, sources)
|
||||
{
|
||||
ComponentMask = compMask;
|
||||
}
|
||||
|
||||
public IAstNode GetSource(int index)
|
||||
|
|
|
@ -7,21 +7,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
public TextureType Type { get; }
|
||||
public TextureFlags Flags { get; }
|
||||
|
||||
public int TextureHandle { get; }
|
||||
public int[] Components { get; }
|
||||
public int Handle { get; }
|
||||
|
||||
public AstTextureOperation(
|
||||
Instruction inst,
|
||||
TextureType type,
|
||||
TextureFlags flags,
|
||||
int textureHandle,
|
||||
int[] components,
|
||||
params IAstNode[] sources) : base(inst, sources)
|
||||
int handle,
|
||||
int compMask,
|
||||
params IAstNode[] sources) : base(inst, compMask, sources)
|
||||
{
|
||||
Type = type;
|
||||
Flags = flags;
|
||||
TextureHandle = textureHandle;
|
||||
Components = components;
|
||||
Type = type;
|
||||
Flags = flags;
|
||||
Handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,9 +79,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.TextureSample, VariableType.F32);
|
||||
Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32);
|
||||
Add(Instruction.Truncate, VariableType.F32, VariableType.F32);
|
||||
Add(Instruction.UnpackHalf2x16High, VariableType.F32, VariableType.U32);
|
||||
Add(Instruction.UnpackHalf2x16Low, VariableType.F32, VariableType.U32);
|
||||
Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32);
|
||||
}
|
||||
|
||||
private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes)
|
||||
|
@ -91,17 +92,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public static VariableType GetDestVarType(Instruction inst)
|
||||
{
|
||||
if (IsTextureInst(inst))
|
||||
{
|
||||
return VariableType.F32;
|
||||
}
|
||||
|
||||
return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst);
|
||||
}
|
||||
|
||||
public static VariableType GetSrcVarType(Instruction inst, int index)
|
||||
{
|
||||
if (IsTextureInst(inst))
|
||||
if (inst == Instruction.TextureSample)
|
||||
{
|
||||
return VariableType.F32;
|
||||
}
|
||||
|
@ -129,22 +125,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
return type;
|
||||
}
|
||||
|
||||
private static bool IsTextureInst(Instruction inst)
|
||||
{
|
||||
return inst == Instruction.TextureSample;
|
||||
}
|
||||
|
||||
public static bool IsUnary(Instruction inst)
|
||||
{
|
||||
if (inst == Instruction.TextureSample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inst == Instruction.Copy)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (inst == Instruction.TextureSample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1;
|
||||
}
|
||||
|
|
|
@ -97,28 +97,27 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
dest.VarType = InstructionInfo.GetDestVarType(inst);
|
||||
}
|
||||
|
||||
int componentMask = 1 << operation.ComponentIndex;
|
||||
|
||||
IAstNode source;
|
||||
|
||||
if (operation is TextureOperation texOp)
|
||||
{
|
||||
if (!context.Info.Samplers.TryAdd(texOp.TextureHandle, texOp.Type))
|
||||
{
|
||||
//TODO: Warning.
|
||||
}
|
||||
|
||||
int[] components = new int[] { texOp.ComponentIndex };
|
||||
|
||||
source = new AstTextureOperation(
|
||||
AstTextureOperation astTexOp = new AstTextureOperation(
|
||||
inst,
|
||||
texOp.Type,
|
||||
texOp.Flags,
|
||||
texOp.TextureHandle,
|
||||
components,
|
||||
texOp.Handle,
|
||||
componentMask,
|
||||
sources);
|
||||
|
||||
context.Info.Samplers.Add(astTexOp);
|
||||
|
||||
source = astTexOp;
|
||||
}
|
||||
else if (!isCopy)
|
||||
{
|
||||
source = new AstOperation(inst, sources);
|
||||
source = new AstOperation(inst, componentMask, sources);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
|
@ -14,7 +13,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
public HashSet<int> IAttributes { get; }
|
||||
public HashSet<int> OAttributes { get; }
|
||||
|
||||
public Dictionary<int, TextureType> Samplers { get; }
|
||||
private HashSet<int> _textureHandles;
|
||||
|
||||
public HashSet<AstTextureOperation> Samplers { get; }
|
||||
|
||||
public StructuredProgramInfo(AstBlock mainBlock)
|
||||
{
|
||||
|
@ -27,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
IAttributes = new HashSet<int>();
|
||||
OAttributes = new HashSet<int>();
|
||||
|
||||
Samplers = new Dictionary<int, TextureType>();
|
||||
Samplers = new HashSet<AstTextureOperation>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -400,12 +400,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.UnpackHalf2x16High, Local(), a);
|
||||
return UnpackHalf2x16(context, a, 1);
|
||||
}
|
||||
|
||||
public static Operand UnpackHalf2x16Low(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.UnpackHalf2x16Low, Local(), a);
|
||||
return UnpackHalf2x16(context, a, 0);
|
||||
}
|
||||
|
||||
private static Operand UnpackHalf2x16(this EmitterContext context, Operand a, int index)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new Operation(Instruction.UnpackHalf2x16, index, dest, a));
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -119,13 +119,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
|
||||
Operand src;
|
||||
|
||||
if (operation.Inst == Instruction.UnpackHalf2x16High)
|
||||
if (operation.Inst == Instruction.UnpackHalf2x16)
|
||||
{
|
||||
src = src1;
|
||||
}
|
||||
else if (operation.Inst == Instruction.UnpackHalf2x16Low)
|
||||
{
|
||||
src = src0;
|
||||
src = operation.ComponentIndex == 1 ? src1 : src0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue