diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs index de589cc1f5..85df7384e6 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public static void Declare(CodeGenContext context, StructuredProgramInfo prgInfo) { - context.AppendLine("#version 410 core"); + context.AppendLine("#version 420 core"); context.AppendLine(); @@ -110,13 +110,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo prgInfo) { - foreach (KeyValuePair kv in prgInfo.Samplers.OrderBy(x => x.Key)) + HashSet samplerNames = new HashSet(); + + foreach (AstTextureOperation texOp in prgInfo.Samplers.OrderBy(x => x.Handle)) { - int textureHandle = kv.Key; + string samplerName = OperandManager.GetSamplerName(context.ShaderType, texOp); - string samplerTypeName = GetSamplerTypeName(kv.Value); + if (!samplerNames.Add(samplerName)) + { + continue; + } - string samplerName = OperandManager.GetSamplerName(context.ShaderType, textureHandle); + string samplerTypeName = GetSamplerTypeName(texOp.Type); context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); } @@ -154,6 +159,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); } + if ((type & TextureType.Multisample) != 0) + { + typeName += "MS"; + } + if ((type & TextureType.Array) != 0) { typeName += "Array"; diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs index 801224a2c7..e19f674b00 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; @@ -82,7 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl PrepareForReturn(context); } - context.AppendLine(Instructions.GetExpression(context, operation) + ";"); + context.AppendLine(InstGen.GetExpression(context, operation) + ";"); } else if (node is AstAssignment assignment) { @@ -97,7 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - dest = Instructions.GetExpression(context, assignment.Destination); + dest = InstGen.GetExpression(context, assignment.Destination); } string src = ReinterpretCast(context, assignment.Source, srcType, dstType); diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions.cs deleted file mode 100644 index 5da6c37225..0000000000 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs new file mode 100644 index 0000000000..8a0d66301d --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -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}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs new file mode 100644 index 0000000000..2ac2b97657 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs new file mode 100644 index 0000000000..d2cc533a6c --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs new file mode 100644 index 0000000000..4a40032c57 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs new file mode 100644 index 0000000000..fc9aef7ec3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs new file mode 100644 index 0000000000..7d38a9d269 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs @@ -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 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs index 10b25f2f3f..4bd8d14a17 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs @@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl case AttributeConsts.PositionX: return "gl_FragCoord.x"; case AttributeConsts.PositionY: return "gl_FragCoord.y"; case AttributeConsts.PositionZ: return "gl_FragCoord.z"; - case AttributeConsts.PositionW: return "1"; + case AttributeConsts.PositionW: return "1.0"; } } @@ -145,9 +145,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return ubName + "_" + DefaultNames.UniformNameSuffix; } - public static string GetSamplerName(GalShaderType shaderType, int textureHandle) + public static string GetSamplerName(GalShaderType shaderType, AstTextureOperation texOp) { - string suffix = (textureHandle - 8).ToString(); + string suffix; + + if ((texOp.Flags & TextureFlags.Bindless) != 0) + { + AstOperand operand = texOp.GetSource(0) as AstOperand; + + suffix = "_cb" + operand.CbufSlot + "_" + operand.CbufOffset; + } + else + { + suffix = (texOp.Handle - 8).ToString(); + } return GetShaderStagePrefix(shaderType) + "_" + DefaultNames.SamplerNamePrefix + suffix; } diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs index a86556bd5b..7adc5ad339 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; @@ -20,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - string expr = Instructions.GetExpression(context, node); + string expr = InstGen.GetExpression(context, node); return ReinterpretCast(expr, node, srcType, dstType); } @@ -55,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (dstType == VariableType.Bool) { - expr = Instructions.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); + expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); return $"({expr} != 0)"; } @@ -76,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); - expr = Instructions.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); + expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); return $"({expr} ? {trueExpr} : {falseExpr})"; } diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs index 6ce654cb4c..d1216fced3 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs @@ -130,8 +130,15 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy)); Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync)); Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex)); + Set("1101111010111x", InstEmit.Tex_B, typeof(OpCodeTex)); Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs)); - Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTex)); + Set("1101101xxxxxxx", InstEmit.Texs, typeof(OpCodeTlds)); + Set("1101111100xxxx", InstEmit.Texs, typeof(OpCodeTld4s)); + Set("1101110000111x", InstEmit.Tld, typeof(OpCodeTld)); + Set("1101110100111x", InstEmit.Tld_B, typeof(OpCodeTld)); + Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4)); + Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); + Set("1101111101010x", InstEmit.Txq_B, typeof(OpCodeTex)); Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf)); Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm)); Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf)); diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs index edaa70bf17..75a28b3a7f 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs @@ -2,45 +2,13 @@ using Ryujinx.Graphics.Shader.Instructions; namespace Ryujinx.Graphics.Shader.Decoders { - class OpCodeTex : OpCode + class OpCodeTex : OpCodeTexture { - public Register Rd { get; } - public Register Ra { get; } - public Register Rb { get; } - - public bool IsArray { get; } - - public TextureDimensions Dimensions { get; } - - public int ComponentMask { get; } - - public int Immediate { get; } - public bool HasDepthCompare { get; } - public bool HasOffset { get; } - - public TextureLodMode LodMode { get; } - public OpCodeTex(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { - Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); - Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); - Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); - - IsArray = opCode.Extract(28); - - Dimensions = (TextureDimensions)opCode.Extract(29, 2); - - ComponentMask = opCode.Extract(31, 4); - - Immediate = opCode.Extract(36, 13); - HasDepthCompare = opCode.Extract(50); - - HasOffset = opCode.Extract(54); - - LodMode = (TextureLodMode)opCode.Extract(55, 3); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs index 2e1826854e..2650bdd2bb 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs @@ -2,67 +2,10 @@ using Ryujinx.Graphics.Shader.Instructions; namespace Ryujinx.Graphics.Shader.Decoders { - class OpCodeTexs : OpCode + class OpCodeTexs : OpCodeTextureScalar { -#region "Component mask LUT" - private const int ____ = 0x0; - private const int R___ = 0x1; - private const int _G__ = 0x2; - private const int RG__ = 0x3; - private const int __B_ = 0x4; - private const int RGB_ = 0x7; - private const int ___A = 0x8; - private const int R__A = 0x9; - private const int _G_A = 0xa; - private const int RG_A = 0xb; - private const int __BA = 0xc; - private const int R_BA = 0xd; - private const int _GBA = 0xe; - private const int RGBA = 0xf; + public TexsType Type => (TexsType)RawType; - private static int[,] _maskLut = new int[,] - { - { ____, ____, ____, ____, ____, ____, ____, ____ }, - { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA }, - { R___, _G__, __B_, ___A, RG__, ____, ____, ____ }, - { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } - }; -#endregion - - public Register Rd0 { get; } - public Register Ra { get; } - public Register Rb { get; } - public Register Rd1 { get; } - - public int Immediate { get; } - - public int ComponentMask { get; } - - public TexsType Type { get; } - - public bool IsFp16 { get; } - - public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) - { - Rd0 = new Register(opCode.Extract(0, 8), RegisterType.Gpr); - Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); - Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); - Rd1 = new Register(opCode.Extract(28, 8), RegisterType.Gpr); - - Immediate = opCode.Extract(36, 13); - - int compSel = opCode.Extract(50, 3); - - Type = (TexsType)opCode.Extract(53, 4); - - IsFp16 = !opCode.Extract(59); - - int rdMask; - - rdMask = Rd0.IsRZ ? 0 : 1; - rdMask |= Rd1.IsRZ ? 0 : 2; - - ComponentMask = _maskLut[rdMask, compSel]; - } + public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs new file mode 100644 index 0000000000..2b617b6602 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs new file mode 100644 index 0000000000..4389f45319 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs @@ -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]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs new file mode 100644 index 0000000000..e0846a05d9 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs new file mode 100644 index 0000000000..f5e1a80e5a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs new file mode 100644 index 0000000000..0d7b84606a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs new file mode 100644 index 0000000000..d69d5ce217 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs @@ -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) { } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TldsType.cs b/Ryujinx.Graphics/Shader/Decoders/TldsType.cs new file mode 100644 index 0000000000..4dd48e646d --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TldsType.cs @@ -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 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs index a8ad282f41..f3a6b79a36 100644 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs @@ -139,13 +139,35 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Tex(EmitterContext context) { - OpCodeTex op = (OpCodeTex)context.CurrOp; + Tex(context, TextureFlags.None); + } - if (op.Rd.IsRZ) + public static void Tex_B(EmitterContext context) + { + Tex(context, TextureFlags.Bindless); + } + + public static void Tld(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords); + } + + public static void Tld_B(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords | TextureFlags.Bindless); + } + + public static void Texs(EmitterContext context) + { + OpCodeTextureScalar op = (OpCodeTextureScalar)context.CurrOp; + + if (op.Rd0.IsRZ && op.Rd1.IsRZ) { return; } + List sourcesList = new List(); + int raIndex = op.Ra.Index; int rbIndex = op.Rb.Index; @@ -169,200 +191,157 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(rbIndex++, RegisterType.Gpr)); } - Operand arrayIndex = op.IsArray ? Ra() : null; + TextureType type; + TextureFlags flags; - List sourcesList = new List(); - - TextureType type = GetTextureType(op.Dimensions); - - TextureFlags flags = TextureFlags.None; - - int elemsCount = GetTextureCoordsCount(type); - - for (int index = 0; index < elemsCount; index++) + if (op is OpCodeTexs texsOp) { - sourcesList.Add(Ra()); - } + type = GetTextureType (texsOp.Type); + flags = GetTextureFlags(texsOp.Type); - if (op.IsArray) - { - sourcesList.Add(arrayIndex); - - type |= TextureType.Array; - } - - bool hasLod = op.LodMode > TextureLodMode.LodZero; - - Operand lodValue = hasLod ? Rb() : ConstF(0); - - Operand packedOffs = op.HasOffset ? Rb() : null; - - if (op.HasDepthCompare) - { - sourcesList.Add(Rb()); - } - - if (op.LodMode == TextureLodMode.LodZero || - op.LodMode == TextureLodMode.LodLevel || - op.LodMode == TextureLodMode.LodLevelA) - { - sourcesList.Add(lodValue); - - flags |= TextureFlags.LodLevel; - } - - if (op.HasOffset) - { - for (int index = 0; index < elemsCount; index++) + if ((type & TextureType.Array) != 0) { - sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4))); + Operand arrayIndex = Ra(); + + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + + sourcesList.Add(arrayIndex); + + if ((type & TextureType.Shadow) != 0) + { + sourcesList.Add(Rb()); + } + + if ((flags & TextureFlags.LodLevel) != 0) + { + sourcesList.Add(ConstF(0)); + } + } + else + { + switch (texsOp.Type) + { + case TexsType.Texture1DLodZero: + sourcesList.Add(Ra()); + break; + + case TexsType.Texture2D: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexsType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TexsType.Texture2DLodLevel: + case TexsType.Texture2DDepthCompare: + case TexsType.Texture3D: + case TexsType.TextureCube: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexsType.Texture2DLodZeroDepthCompare: + case TexsType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TexsType.Texture2DLodLevelDepthCompare: + case TexsType.TextureCubeLodLevel: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + break; + } + } + } + else if (op is OpCodeTlds tldsOp) + { + type = GetTextureType (tldsOp.Type); + flags = GetTextureFlags(tldsOp.Type) | TextureFlags.IntCoords; + + switch (tldsOp.Type) + { + case TldsType.Texture1DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(ConstF(0)); + break; + + case TldsType.Texture1DLodLevel: + case TldsType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TldsType.Texture2DLodZeroOffset: + case TldsType.Texture2DLodLevel: + case TldsType.Texture2DLodZeroMultisample: + case TldsType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TldsType.Texture2DArrayLodZero: + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + sourcesList.Add(Ra()); + break; + + case TldsType.Texture2DLodLevelOffset: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + break; + } + } + else if (op is OpCodeTld4s tld4sOp) + { + if (!(tld4sOp.HasDepthCompare || tld4sOp.HasOffset)) + { + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + } + else + { + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); } - flags |= TextureFlags.Offset; + type = TextureType.Texture2D; + flags = TextureFlags.Gather; + + if (tld4sOp.HasOffset) + { + sourcesList.Add(Rb()); + + flags |= TextureFlags.Offset; + } + + if (tld4sOp.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } } - - if (op.LodMode == TextureLodMode.LodBias || - op.LodMode == TextureLodMode.LodBiasA) + else { - sourcesList.Add(lodValue); - - flags |= TextureFlags.LodBias; + throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\"."); } Operand[] sources = sourcesList.ToArray(); - int rdIndex = op.Rd.Index; - - Operand GetDest() - { - if (rdIndex > RegisterConsts.RegisterZeroIndex) - { - return Const(0); - } - - return Register(rdIndex++, RegisterType.Gpr); - } - - int textureHandle = op.Immediate; - - for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) - { - if ((compMask & 1) != 0) - { - Operand dest = GetDest(); - - TextureOperation operation = new TextureOperation( - Instruction.TextureSample, - type, - flags, - textureHandle, - compIndex, - dest, - sources); - - context.Add(operation); - } - } - } - - public static void Texs(EmitterContext context) - { - OpCodeTexs op = (OpCodeTexs)context.CurrOp; - - if (op.Rd0.IsRZ && op.Rd1.IsRZ) - { - return; - } - - List sourcesList = new List(); - - Operand Src(int index) - { - int regIndex = 0; - - switch (index & 2) - { - case 0: regIndex = op.Ra.Index; break; - case 2: regIndex = op.Rb.Index; break; - } - - if (regIndex != RegisterConsts.RegisterZeroIndex) - { - regIndex += index & 1; - } - - return context.Copy(Register(regIndex, RegisterType.Gpr)); - } - - switch (op.Type) - { - case TexsType.Texture1DLodZero: - sourcesList.Add(Src(0)); - break; - - case TexsType.Texture2D: - sourcesList.Add(Src(0)); - sourcesList.Add(Src(2)); - break; - - case TexsType.Texture2DLodZero: - sourcesList.Add(Src(0)); - sourcesList.Add(Src(2)); - sourcesList.Add(ConstF(0)); - break; - - case TexsType.Texture2DLodLevel: - case TexsType.Texture2DDepthCompare: - case TexsType.Texture3D: - case TexsType.TextureCube: - sourcesList.Add(Src(0)); - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - break; - - case TexsType.Texture2DLodZeroDepthCompare: - case TexsType.Texture3DLodZero: - sourcesList.Add(Src(0)); - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - sourcesList.Add(ConstF(0)); - break; - - case TexsType.Texture2DLodLevelDepthCompare: - case TexsType.TextureCubeLodLevel: - sourcesList.Add(Src(0)); - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - sourcesList.Add(Src(3)); - break; - - case TexsType.Texture2DArray: - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - sourcesList.Add(Src(0)); - break; - - case TexsType.Texture2DArrayLodZero: - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - sourcesList.Add(Src(0)); - sourcesList.Add(ConstF(0)); - break; - - case TexsType.Texture2DArrayLodZeroDepthCompare: - sourcesList.Add(Src(1)); - sourcesList.Add(Src(2)); - sourcesList.Add(Src(0)); - sourcesList.Add(Src(3)); - sourcesList.Add(ConstF(0)); - break; - } - - Operand[] sources = sourcesList.ToArray(); - - TextureType type = GetTextureType (op.Type); - TextureFlags flags = GetTextureFlags(op.Type); - Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) }; Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) }; @@ -394,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - int textureHandle = op.Immediate; + int handle = op.Immediate; for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { @@ -406,7 +385,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Instruction.TextureSample, type, flags, - textureHandle, + handle, compIndex, dest, sources); @@ -424,17 +403,13 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Tld4(EmitterContext context) { - OpCodeTex op = (OpCodeTex)context.CurrOp; + OpCodeTld4 op = (OpCodeTld4)context.CurrOp; if (op.Rd.IsRZ) { return; } - TextureGatherOffset offset = (TextureGatherOffset)op.RawOpCode.Extract(54, 2); - - int gatherCompIndex = op.RawOpCode.Extract(56, 2); - int raIndex = op.Ra.Index; int rbIndex = op.Rb.Index; @@ -466,9 +441,9 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFlags flags = TextureFlags.Gather; - int elemsCount = GetTextureCoordsCount(type); + int coordsCount = type.GetCoordsCount(); - for (int index = 0; index < elemsCount; index++) + for (int index = 0; index < coordsCount; index++) { sourcesList.Add(Ra()); } @@ -482,31 +457,31 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand[] packedOffs = new Operand[2]; - packedOffs[0] = offset != TextureGatherOffset.None ? Rb() : null; - packedOffs[1] = offset == TextureGatherOffset.Offsets ? Rb() : null; + packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null; + packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null; if (op.HasDepthCompare) { sourcesList.Add(Rb()); } - if (offset != TextureGatherOffset.None) + if (op.Offset != TextureGatherOffset.None) { - int offsetTexelsCount = offset == TextureGatherOffset.Offsets ? 4 : 1; + int offsetTexelsCount = op.Offset == TextureGatherOffset.Offsets ? 4 : 1; - for (int index = 0; index < elemsCount * offsetTexelsCount; index++) + for (int index = 0; index < coordsCount * offsetTexelsCount; index++) { Operand packed = packedOffs[(index >> 2) & 1]; sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6))); } - flags |= offset == TextureGatherOffset.Offsets + flags |= op.Offset == TextureGatherOffset.Offsets ? TextureFlags.Offsets : TextureFlags.Offset; } - sourcesList.Add(Const(gatherCompIndex)); + sourcesList.Add(Const(op.GatherCompIndex)); Operand[] sources = sourcesList.ToArray(); @@ -522,7 +497,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return Register(rdIndex++, RegisterType.Gpr); } - int textureHandle = op.Immediate; + int handle = op.Immediate; for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { @@ -534,7 +509,231 @@ namespace Ryujinx.Graphics.Shader.Instructions Instruction.TextureSample, type, flags, - textureHandle, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + } + + public static void Txq(EmitterContext context) + { + Txq(context, bindless: false); + } + + public static void Txq_B(EmitterContext context) + { + Txq(context, bindless: true); + } + + private static void Txq(EmitterContext context, bool bindless) + { + OpCodeTex op = (OpCodeTex)context.CurrOp; + + if (op.Rd.IsRZ) + { + return; + } + + TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6); + + //TODO: Validate and use property. + Instruction inst = Instruction.TextureSize; + + TextureType type = TextureType.Texture2D; + + TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None; + + int raIndex = op.Ra.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + List sourcesList = new List(); + + 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 sourcesList = new List(); + + if (isBindless) + { + sourcesList.Add(Rb()); + } + + TextureType type = GetTextureType(op.Dimensions); + + int coordsCount = type.GetCoordsCount(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (op.IsArray) + { + sourcesList.Add(arrayIndex); + + type |= TextureType.Array; + } + + bool hasLod = op.LodMode > TextureLodMode.LodZero; + + Operand lodValue = hasLod ? Rb() : ConstF(0); + + Operand packedOffs = op.HasOffset ? Rb() : null; + + if (op is OpCodeTex texOp && texOp.HasDepthCompare) + { + sourcesList.Add(Rb()); + } + + if (op.LodMode == TextureLodMode.LodZero || + op.LodMode == TextureLodMode.LodLevel || + op.LodMode == TextureLodMode.LodLevelA) + { + sourcesList.Add(lodValue); + + flags |= TextureFlags.LodLevel; + } + + if (op.HasOffset) + { + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4))); + } + + flags |= TextureFlags.Offset; + } + + if (op.LodMode == TextureLodMode.LodBias || + op.LodMode == TextureLodMode.LodBiasA) + { + sourcesList.Add(lodValue); + + flags |= TextureFlags.LodBias; + } + + if (op is OpCodeTld tldOp && tldOp.IsMultisample) + { + sourcesList.Add(Rb()); + } + + Operand[] sources = sourcesList.ToArray(); + + int rdIndex = op.Rd.Index; + + Operand GetDest() + { + if (rdIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return Register(rdIndex++, RegisterType.Gpr); + } + + int handle = !isBindless ? op.Immediate : 0; + + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) != 0) + { + Operand dest = GetDest(); + + TextureOperation operation = new TextureOperation( + Instruction.TextureSample, + type, + flags, + handle, compIndex, dest, sources); @@ -557,19 +756,6 @@ namespace Ryujinx.Graphics.Shader.Instructions throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."); } - private static int GetTextureCoordsCount(TextureType type) - { - switch (type & TextureType.Mask) - { - case TextureType.Texture1D: return 1; - case TextureType.Texture2D: return 2; - case TextureType.Texture3D: return 3; - case TextureType.TextureCube: return 3; - } - - throw new ArgumentException($"Invalid texture type \"{type}\"."); - } - private static TextureType GetTextureType(TexsType type) { switch (type) @@ -606,6 +792,33 @@ namespace Ryujinx.Graphics.Shader.Instructions throw new ArgumentException($"Invalid texture type \"{type}\"."); } + private static TextureType GetTextureType(TldsType type) + { + switch (type) + { + case TldsType.Texture1DLodZero: + case TldsType.Texture1DLodLevel: + return TextureType.Texture1D; + + case TldsType.Texture2DLodZero: + case TldsType.Texture2DLodZeroOffset: + case TldsType.Texture2DLodLevel: + case TldsType.Texture2DLodLevelOffset: + return TextureType.Texture2D; + + case TldsType.Texture2DLodZeroMultisample: + return TextureType.Texture2D | TextureType.Multisample; + + case TldsType.Texture3DLodZero: + return TextureType.Texture3D; + + case TldsType.Texture2DArrayLodZero: + return TextureType.Texture2D | TextureType.Array; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + private static TextureFlags GetTextureFlags(TexsType type) { switch (type) @@ -631,5 +844,26 @@ namespace Ryujinx.Graphics.Shader.Instructions throw new ArgumentException($"Invalid texture type \"{type}\"."); } + + private static TextureFlags GetTextureFlags(TldsType type) + { + switch (type) + { + case TldsType.Texture1DLodZero: + case TldsType.Texture1DLodLevel: + case TldsType.Texture2DLodZero: + case TldsType.Texture2DLodLevel: + case TldsType.Texture2DLodZeroMultisample: + case TldsType.Texture3DLodZero: + case TldsType.Texture2DArrayLodZero: + return TextureFlags.LodLevel; + + case TldsType.Texture2DLodZeroOffset: + case TldsType.Texture2DLodLevelOffset: + return TextureFlags.LodLevel | TextureFlags.Offset; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/TextureProperty.cs b/Ryujinx.Graphics/Shader/Instructions/TextureProperty.cs new file mode 100644 index 0000000000..476976e1f1 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/TextureProperty.cs @@ -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 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs index f5219d8be5..ac0ebc2b08 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs @@ -75,11 +75,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation StoreLocal, Subtract, TextureSample, + TextureSize, Truncate, - UnpackDouble2x32High, - UnpackDouble2x32Low, - UnpackHalf2x16High, - UnpackHalf2x16Low, + UnpackDouble2x32, + UnpackHalf2x16, Count, FP = 1 << 16, diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs index 96c210a084..ad47d9048d 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public int SourcesCount => _sources.Length; + public int ComponentIndex { get; } + public Operation(Instruction inst, Operand dest, params Operand[] sources) { Inst = inst; @@ -33,6 +35,15 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation } } + public Operation( + Instruction inst, + int compIndex, + Operand dest, + params Operand[] sources) : this(inst, dest, sources) + { + ComponentIndex = compIndex; + } + private Operand AssignDest(Operand dest) { if (dest != null && dest.Type == OperandType.LocalVariable) diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs index cfe733f105..5f0a84276c 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs @@ -5,11 +5,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation [Flags] enum TextureFlags { - None = 0, - Gather = 1 << 0, - LodBias = 1 << 1, - LodLevel = 1 << 2, - Offset = 1 << 3, - Offsets = 1 << 4 + None = 0, + Bindless = 1 << 0, + Gather = 1 << 1, + IntCoords = 1 << 2, + LodBias = 1 << 3, + LodLevel = 1 << 4, + Offset = 1 << 5, + Offsets = 1 << 6 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs index 177fcca961..f5f2cc5c60 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs @@ -5,22 +5,20 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public TextureType Type { get; } public TextureFlags Flags { get; } - public int TextureHandle { get; } - public int ComponentIndex { get; } + public int Handle { get; } public TextureOperation( Instruction inst, TextureType type, TextureFlags flags, - int textureHandle, - int componentIndex, + int handle, + int compIndex, Operand dest, - params Operand[] sources) : base(inst, dest, sources) + params Operand[] sources) : base(inst, compIndex, dest, sources) { - Type = type; - Flags = flags; - TextureHandle = textureHandle; - ComponentIndex = componentIndex; + Type = type; + Flags = flags; + Handle = handle; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs index 8d89ac8fd0..bf20700776 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs @@ -12,7 +12,24 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Mask = 0xff, - Array = 1 << 8, - Shadow = 1 << 9 + Array = 1 << 8, + Multisample = 1 << 9, + Shadow = 1 << 10 + } + + static class TextureTypeExtensions + { + public static int GetCoordsCount(this TextureType type) + { + switch (type & TextureType.Mask) + { + case TextureType.Texture1D: return 1; + case TextureType.Texture2D: return 2; + case TextureType.Texture3D: return 3; + case TextureType.TextureCube: return 3; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs index 8594fe6d64..1607ffecde 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public Instruction Inst { get; } + public int ComponentMask { get; } + private IAstNode[] _sources; public int SourcesCount => _sources.Length; @@ -21,6 +23,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AddUse(source, this); } + + ComponentMask = 1; + } + + public AstOperation(Instruction inst, int compMask, params IAstNode[] sources) : this(inst, sources) + { + ComponentMask = compMask; } public IAstNode GetSource(int index) diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs index c197859f21..e40f7b70eb 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs @@ -7,21 +7,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public TextureType Type { get; } public TextureFlags Flags { get; } - public int TextureHandle { get; } - public int[] Components { get; } + public int Handle { get; } public AstTextureOperation( Instruction inst, TextureType type, TextureFlags flags, - int textureHandle, - int[] components, - params IAstNode[] sources) : base(inst, sources) + int handle, + int compMask, + params IAstNode[] sources) : base(inst, compMask, sources) { - Type = type; - Flags = flags; - TextureHandle = textureHandle; - Components = components; + Type = type; + Flags = flags; + Handle = handle; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs index 278701f930..46a61553b8 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs @@ -79,9 +79,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.TextureSample, VariableType.F32); + Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32); Add(Instruction.Truncate, VariableType.F32, VariableType.F32); - Add(Instruction.UnpackHalf2x16High, VariableType.F32, VariableType.U32); - Add(Instruction.UnpackHalf2x16Low, VariableType.F32, VariableType.U32); + Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32); } private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes) @@ -91,17 +92,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public static VariableType GetDestVarType(Instruction inst) { - if (IsTextureInst(inst)) - { - return VariableType.F32; - } - return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst); } public static VariableType GetSrcVarType(Instruction inst, int index) { - if (IsTextureInst(inst)) + if (inst == Instruction.TextureSample) { return VariableType.F32; } @@ -129,22 +125,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return type; } - private static bool IsTextureInst(Instruction inst) - { - return inst == Instruction.TextureSample; - } - public static bool IsUnary(Instruction inst) { - if (inst == Instruction.TextureSample) - { - return false; - } - if (inst == Instruction.Copy) { return true; } + else if (inst == Instruction.TextureSample) + { + return false; + } return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1; } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs index 888bdcc853..7a5bdac0c9 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs @@ -97,28 +97,27 @@ namespace Ryujinx.Graphics.Shader.StructuredIr dest.VarType = InstructionInfo.GetDestVarType(inst); } + int componentMask = 1 << operation.ComponentIndex; + IAstNode source; if (operation is TextureOperation texOp) { - if (!context.Info.Samplers.TryAdd(texOp.TextureHandle, texOp.Type)) - { - //TODO: Warning. - } - - int[] components = new int[] { texOp.ComponentIndex }; - - source = new AstTextureOperation( + AstTextureOperation astTexOp = new AstTextureOperation( inst, texOp.Type, texOp.Flags, - texOp.TextureHandle, - components, + texOp.Handle, + componentMask, sources); + + context.Info.Samplers.Add(astTexOp); + + source = astTexOp; } else if (!isCopy) { - source = new AstOperation(inst, sources); + source = new AstOperation(inst, componentMask, sources); } else { diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs index d8f4377692..203d39ea6b 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -14,7 +13,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HashSet IAttributes { get; } public HashSet OAttributes { get; } - public Dictionary Samplers { get; } + private HashSet _textureHandles; + + public HashSet Samplers { get; } public StructuredProgramInfo(AstBlock mainBlock) { @@ -27,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr IAttributes = new HashSet(); OAttributes = new HashSet(); - Samplers = new Dictionary(); + Samplers = new HashSet(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs index d0b54961a4..604aa67d34 100644 --- a/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs @@ -400,12 +400,21 @@ namespace Ryujinx.Graphics.Shader.Translation public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a) { - return context.Add(Instruction.UnpackHalf2x16High, Local(), a); + return UnpackHalf2x16(context, a, 1); } public static Operand UnpackHalf2x16Low(this EmitterContext context, Operand a) { - return context.Add(Instruction.UnpackHalf2x16Low, Local(), a); + return UnpackHalf2x16(context, a, 0); + } + + private static Operand UnpackHalf2x16(this EmitterContext context, Operand a, int index) + { + Operand dest = Local(); + + context.Add(new Operation(Instruction.UnpackHalf2x16, index, dest, a)); + + return dest; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs index 050fc48af3..70714c84b9 100644 --- a/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs @@ -119,13 +119,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand src; - if (operation.Inst == Instruction.UnpackHalf2x16High) + if (operation.Inst == Instruction.UnpackHalf2x16) { - src = src1; - } - else if (operation.Inst == Instruction.UnpackHalf2x16Low) - { - src = src0; + src = operation.ComponentIndex == 1 ? src1 : src0; } else {