Add TXQ, TLD, TLDS and TLD4S shader texture instructions, and some support for bindless textures, some refactoring on codegen

This commit is contained in:
gdkchan 2019-04-09 21:32:21 -03:00
parent ce71abeec9
commit 9f4e21369e
35 changed files with 1388 additions and 907 deletions

View file

@ -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";

View file

@ -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);

View file

@ -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;
}
}
}

View 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}\".");
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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;
}

View file

@ -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})";
}

View file

@ -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));

View file

@ -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);
}
}
}

View file

@ -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) { }
}
}

View 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);
}
}
}

View 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];
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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) { }
}
}

View 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
}
}

View file

@ -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}\".");
}
}
}

View 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
}
}

View file

@ -75,11 +75,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
StoreLocal,
Subtract,
TextureSample,
TextureSize,
Truncate,
UnpackDouble2x32High,
UnpackDouble2x32Low,
UnpackHalf2x16High,
UnpackHalf2x16Low,
UnpackDouble2x32,
UnpackHalf2x16,
Count,
FP = 1 << 16,

View file

@ -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)

View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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
{

View file

@ -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>();
}
}
}

View file

@ -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;
}
}
}

View file

@ -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
{