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)
|
public static void Declare(CodeGenContext context, StructuredProgramInfo prgInfo)
|
||||||
{
|
{
|
||||||
context.AppendLine("#version 410 core");
|
context.AppendLine("#version 420 core");
|
||||||
|
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
|
|
||||||
|
@ -110,13 +110,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo prgInfo)
|
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 + ";");
|
context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";");
|
||||||
}
|
}
|
||||||
|
@ -154,6 +159,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((type & TextureType.Multisample) != 0)
|
||||||
|
{
|
||||||
|
typeName += "MS";
|
||||||
|
}
|
||||||
|
|
||||||
if ((type & TextureType.Array) != 0)
|
if ((type & TextureType.Array) != 0)
|
||||||
{
|
{
|
||||||
typeName += "Array";
|
typeName += "Array";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using System;
|
using System;
|
||||||
|
@ -82,7 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
PrepareForReturn(context);
|
PrepareForReturn(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AppendLine(Instructions.GetExpression(context, operation) + ";");
|
context.AppendLine(InstGen.GetExpression(context, operation) + ";");
|
||||||
}
|
}
|
||||||
else if (node is AstAssignment assignment)
|
else if (node is AstAssignment assignment)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dest = Instructions.GetExpression(context, assignment.Destination);
|
dest = InstGen.GetExpression(context, assignment.Destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
string src = ReinterpretCast(context, assignment.Source, srcType, dstType);
|
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.PositionX: return "gl_FragCoord.x";
|
||||||
case AttributeConsts.PositionY: return "gl_FragCoord.y";
|
case AttributeConsts.PositionY: return "gl_FragCoord.y";
|
||||||
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
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;
|
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;
|
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.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using System;
|
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);
|
return ReinterpretCast(expr, node, srcType, dstType);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
}
|
}
|
||||||
else if (dstType == VariableType.Bool)
|
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)";
|
return $"({expr} != 0)";
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType);
|
string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType);
|
||||||
string falseExpr = NumberFormatter.FormatInt(IrConsts.False, 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})";
|
return $"({expr} ? {trueExpr} : {falseExpr})";
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,8 +130,15 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy));
|
Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy));
|
||||||
Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync));
|
Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync));
|
||||||
Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
|
Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
|
||||||
|
Set("1101111010111x", InstEmit.Tex_B, typeof(OpCodeTex));
|
||||||
Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs));
|
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("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf));
|
||||||
Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm));
|
Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm));
|
||||||
Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf));
|
Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf));
|
||||||
|
|
|
@ -2,45 +2,13 @@ using Ryujinx.Graphics.Shader.Instructions;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Decoders
|
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 HasDepthCompare { get; }
|
||||||
|
|
||||||
public bool HasOffset { get; }
|
|
||||||
|
|
||||||
public TextureLodMode LodMode { get; }
|
|
||||||
|
|
||||||
public OpCodeTex(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
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);
|
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
|
namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
{
|
{
|
||||||
class OpCodeTexs : OpCode
|
class OpCodeTexs : OpCodeTextureScalar
|
||||||
{
|
{
|
||||||
#region "Component mask LUT"
|
public TexsType Type => (TexsType)RawType;
|
||||||
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[,]
|
public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { }
|
||||||
{
|
|
||||||
{ ____, ____, ____, ____, ____, ____, ____, ____ },
|
|
||||||
{ 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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Operand> sourcesList = new List<Operand>();
|
||||||
|
|
||||||
int raIndex = op.Ra.Index;
|
int raIndex = op.Ra.Index;
|
||||||
int rbIndex = op.Rb.Index;
|
int rbIndex = op.Rb.Index;
|
||||||
|
|
||||||
|
@ -169,200 +191,157 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
|
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand arrayIndex = op.IsArray ? Ra() : null;
|
TextureType type;
|
||||||
|
TextureFlags flags;
|
||||||
|
|
||||||
List<Operand> sourcesList = new List<Operand>();
|
if (op is OpCodeTexs texsOp)
|
||||||
|
|
||||||
TextureType type = GetTextureType(op.Dimensions);
|
|
||||||
|
|
||||||
TextureFlags flags = TextureFlags.None;
|
|
||||||
|
|
||||||
int elemsCount = GetTextureCoordsCount(type);
|
|
||||||
|
|
||||||
for (int index = 0; index < elemsCount; index++)
|
|
||||||
{
|
{
|
||||||
sourcesList.Add(Ra());
|
type = GetTextureType (texsOp.Type);
|
||||||
}
|
flags = GetTextureFlags(texsOp.Type);
|
||||||
|
|
||||||
if (op.IsArray)
|
if ((type & TextureType.Array) != 0)
|
||||||
{
|
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (op.LodMode == TextureLodMode.LodBias ||
|
|
||||||
op.LodMode == TextureLodMode.LodBiasA)
|
|
||||||
{
|
{
|
||||||
sourcesList.Add(lodValue);
|
throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\".");
|
||||||
|
|
||||||
flags |= TextureFlags.LodBias;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand[] sources = sourcesList.ToArray();
|
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[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
|
||||||
Operand[] rd1 = 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++)
|
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||||
{
|
{
|
||||||
|
@ -406,7 +385,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
Instruction.TextureSample,
|
Instruction.TextureSample,
|
||||||
type,
|
type,
|
||||||
flags,
|
flags,
|
||||||
textureHandle,
|
handle,
|
||||||
compIndex,
|
compIndex,
|
||||||
dest,
|
dest,
|
||||||
sources);
|
sources);
|
||||||
|
@ -424,17 +403,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
public static void Tld4(EmitterContext context)
|
public static void Tld4(EmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeTex op = (OpCodeTex)context.CurrOp;
|
OpCodeTld4 op = (OpCodeTld4)context.CurrOp;
|
||||||
|
|
||||||
if (op.Rd.IsRZ)
|
if (op.Rd.IsRZ)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureGatherOffset offset = (TextureGatherOffset)op.RawOpCode.Extract(54, 2);
|
|
||||||
|
|
||||||
int gatherCompIndex = op.RawOpCode.Extract(56, 2);
|
|
||||||
|
|
||||||
int raIndex = op.Ra.Index;
|
int raIndex = op.Ra.Index;
|
||||||
int rbIndex = op.Rb.Index;
|
int rbIndex = op.Rb.Index;
|
||||||
|
|
||||||
|
@ -466,9 +441,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
TextureFlags flags = TextureFlags.Gather;
|
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());
|
sourcesList.Add(Ra());
|
||||||
}
|
}
|
||||||
|
@ -482,31 +457,31 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
Operand[] packedOffs = new Operand[2];
|
Operand[] packedOffs = new Operand[2];
|
||||||
|
|
||||||
packedOffs[0] = offset != TextureGatherOffset.None ? Rb() : null;
|
packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null;
|
||||||
packedOffs[1] = offset == TextureGatherOffset.Offsets ? Rb() : null;
|
packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null;
|
||||||
|
|
||||||
if (op.HasDepthCompare)
|
if (op.HasDepthCompare)
|
||||||
{
|
{
|
||||||
sourcesList.Add(Rb());
|
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];
|
Operand packed = packedOffs[(index >> 2) & 1];
|
||||||
|
|
||||||
sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6)));
|
sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6)));
|
||||||
}
|
}
|
||||||
|
|
||||||
flags |= offset == TextureGatherOffset.Offsets
|
flags |= op.Offset == TextureGatherOffset.Offsets
|
||||||
? TextureFlags.Offsets
|
? TextureFlags.Offsets
|
||||||
: TextureFlags.Offset;
|
: TextureFlags.Offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
sourcesList.Add(Const(gatherCompIndex));
|
sourcesList.Add(Const(op.GatherCompIndex));
|
||||||
|
|
||||||
Operand[] sources = sourcesList.ToArray();
|
Operand[] sources = sourcesList.ToArray();
|
||||||
|
|
||||||
|
@ -522,7 +497,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
return Register(rdIndex++, RegisterType.Gpr);
|
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++)
|
for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||||
{
|
{
|
||||||
|
@ -534,7 +509,231 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
Instruction.TextureSample,
|
Instruction.TextureSample,
|
||||||
type,
|
type,
|
||||||
flags,
|
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,
|
compIndex,
|
||||||
dest,
|
dest,
|
||||||
sources);
|
sources);
|
||||||
|
@ -557,19 +756,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".");
|
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)
|
private static TextureType GetTextureType(TexsType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -606,6 +792,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
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)
|
private static TextureFlags GetTextureFlags(TexsType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -631,5 +844,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
throw new ArgumentException($"Invalid texture type \"{type}\".");
|
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,
|
StoreLocal,
|
||||||
Subtract,
|
Subtract,
|
||||||
TextureSample,
|
TextureSample,
|
||||||
|
TextureSize,
|
||||||
Truncate,
|
Truncate,
|
||||||
UnpackDouble2x32High,
|
UnpackDouble2x32,
|
||||||
UnpackDouble2x32Low,
|
UnpackHalf2x16,
|
||||||
UnpackHalf2x16High,
|
|
||||||
UnpackHalf2x16Low,
|
|
||||||
|
|
||||||
Count,
|
Count,
|
||||||
FP = 1 << 16,
|
FP = 1 << 16,
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
|
||||||
public int SourcesCount => _sources.Length;
|
public int SourcesCount => _sources.Length;
|
||||||
|
|
||||||
|
public int ComponentIndex { get; }
|
||||||
|
|
||||||
public Operation(Instruction inst, Operand dest, params Operand[] sources)
|
public Operation(Instruction inst, Operand dest, params Operand[] sources)
|
||||||
{
|
{
|
||||||
Inst = inst;
|
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)
|
private Operand AssignDest(Operand dest)
|
||||||
{
|
{
|
||||||
if (dest != null && dest.Type == OperandType.LocalVariable)
|
if (dest != null && dest.Type == OperandType.LocalVariable)
|
||||||
|
|
|
@ -5,11 +5,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
[Flags]
|
[Flags]
|
||||||
enum TextureFlags
|
enum TextureFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Gather = 1 << 0,
|
Bindless = 1 << 0,
|
||||||
LodBias = 1 << 1,
|
Gather = 1 << 1,
|
||||||
LodLevel = 1 << 2,
|
IntCoords = 1 << 2,
|
||||||
Offset = 1 << 3,
|
LodBias = 1 << 3,
|
||||||
Offsets = 1 << 4
|
LodLevel = 1 << 4,
|
||||||
|
Offset = 1 << 5,
|
||||||
|
Offsets = 1 << 6
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,22 +5,20 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
public TextureType Type { get; }
|
public TextureType Type { get; }
|
||||||
public TextureFlags Flags { get; }
|
public TextureFlags Flags { get; }
|
||||||
|
|
||||||
public int TextureHandle { get; }
|
public int Handle { get; }
|
||||||
public int ComponentIndex { get; }
|
|
||||||
|
|
||||||
public TextureOperation(
|
public TextureOperation(
|
||||||
Instruction inst,
|
Instruction inst,
|
||||||
TextureType type,
|
TextureType type,
|
||||||
TextureFlags flags,
|
TextureFlags flags,
|
||||||
int textureHandle,
|
int handle,
|
||||||
int componentIndex,
|
int compIndex,
|
||||||
Operand dest,
|
Operand dest,
|
||||||
params Operand[] sources) : base(inst, dest, sources)
|
params Operand[] sources) : base(inst, compIndex, dest, sources)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
TextureHandle = textureHandle;
|
Handle = handle;
|
||||||
ComponentIndex = componentIndex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,24 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
|
||||||
Mask = 0xff,
|
Mask = 0xff,
|
||||||
|
|
||||||
Array = 1 << 8,
|
Array = 1 << 8,
|
||||||
Shadow = 1 << 9
|
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 Instruction Inst { get; }
|
||||||
|
|
||||||
|
public int ComponentMask { get; }
|
||||||
|
|
||||||
private IAstNode[] _sources;
|
private IAstNode[] _sources;
|
||||||
|
|
||||||
public int SourcesCount => _sources.Length;
|
public int SourcesCount => _sources.Length;
|
||||||
|
@ -21,6 +23,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
AddUse(source, this);
|
AddUse(source, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ComponentMask = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AstOperation(Instruction inst, int compMask, params IAstNode[] sources) : this(inst, sources)
|
||||||
|
{
|
||||||
|
ComponentMask = compMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAstNode GetSource(int index)
|
public IAstNode GetSource(int index)
|
||||||
|
|
|
@ -7,21 +7,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
public TextureType Type { get; }
|
public TextureType Type { get; }
|
||||||
public TextureFlags Flags { get; }
|
public TextureFlags Flags { get; }
|
||||||
|
|
||||||
public int TextureHandle { get; }
|
public int Handle { get; }
|
||||||
public int[] Components { get; }
|
|
||||||
|
|
||||||
public AstTextureOperation(
|
public AstTextureOperation(
|
||||||
Instruction inst,
|
Instruction inst,
|
||||||
TextureType type,
|
TextureType type,
|
||||||
TextureFlags flags,
|
TextureFlags flags,
|
||||||
int textureHandle,
|
int handle,
|
||||||
int[] components,
|
int compMask,
|
||||||
params IAstNode[] sources) : base(inst, sources)
|
params IAstNode[] sources) : base(inst, compMask, sources)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
TextureHandle = textureHandle;
|
Handle = handle;
|
||||||
Components = components;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -79,9 +79,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.Subtract, VariableType.Scalar, 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.Truncate, VariableType.F32, VariableType.F32);
|
||||||
Add(Instruction.UnpackHalf2x16High, VariableType.F32, VariableType.U32);
|
Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32);
|
||||||
Add(Instruction.UnpackHalf2x16Low, VariableType.F32, VariableType.U32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes)
|
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)
|
public static VariableType GetDestVarType(Instruction inst)
|
||||||
{
|
{
|
||||||
if (IsTextureInst(inst))
|
|
||||||
{
|
|
||||||
return VariableType.F32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst);
|
return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VariableType GetSrcVarType(Instruction inst, int index)
|
public static VariableType GetSrcVarType(Instruction inst, int index)
|
||||||
{
|
{
|
||||||
if (IsTextureInst(inst))
|
if (inst == Instruction.TextureSample)
|
||||||
{
|
{
|
||||||
return VariableType.F32;
|
return VariableType.F32;
|
||||||
}
|
}
|
||||||
|
@ -129,22 +125,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsTextureInst(Instruction inst)
|
|
||||||
{
|
|
||||||
return inst == Instruction.TextureSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsUnary(Instruction inst)
|
public static bool IsUnary(Instruction inst)
|
||||||
{
|
{
|
||||||
if (inst == Instruction.TextureSample)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst == Instruction.Copy)
|
if (inst == Instruction.Copy)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (inst == Instruction.TextureSample)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1;
|
return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,28 +97,27 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
dest.VarType = InstructionInfo.GetDestVarType(inst);
|
dest.VarType = InstructionInfo.GetDestVarType(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int componentMask = 1 << operation.ComponentIndex;
|
||||||
|
|
||||||
IAstNode source;
|
IAstNode source;
|
||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
if (operation is TextureOperation texOp)
|
||||||
{
|
{
|
||||||
if (!context.Info.Samplers.TryAdd(texOp.TextureHandle, texOp.Type))
|
AstTextureOperation astTexOp = new AstTextureOperation(
|
||||||
{
|
|
||||||
//TODO: Warning.
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] components = new int[] { texOp.ComponentIndex };
|
|
||||||
|
|
||||||
source = new AstTextureOperation(
|
|
||||||
inst,
|
inst,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
texOp.TextureHandle,
|
texOp.Handle,
|
||||||
components,
|
componentMask,
|
||||||
sources);
|
sources);
|
||||||
|
|
||||||
|
context.Info.Samplers.Add(astTexOp);
|
||||||
|
|
||||||
|
source = astTexOp;
|
||||||
}
|
}
|
||||||
else if (!isCopy)
|
else if (!isCopy)
|
||||||
{
|
{
|
||||||
source = new AstOperation(inst, sources);
|
source = new AstOperation(inst, componentMask, sources);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
@ -14,7 +13,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
public HashSet<int> IAttributes { get; }
|
public HashSet<int> IAttributes { get; }
|
||||||
public HashSet<int> OAttributes { 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)
|
public StructuredProgramInfo(AstBlock mainBlock)
|
||||||
{
|
{
|
||||||
|
@ -27,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
IAttributes = new HashSet<int>();
|
IAttributes = new HashSet<int>();
|
||||||
OAttributes = 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)
|
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)
|
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;
|
Operand src;
|
||||||
|
|
||||||
if (operation.Inst == Instruction.UnpackHalf2x16High)
|
if (operation.Inst == Instruction.UnpackHalf2x16)
|
||||||
{
|
{
|
||||||
src = src1;
|
src = operation.ComponentIndex == 1 ? src1 : src0;
|
||||||
}
|
|
||||||
else if (operation.Inst == Instruction.UnpackHalf2x16Low)
|
|
||||||
{
|
|
||||||
src = src0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue