diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index dcb6b1f6..be93f137 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -231,6 +231,8 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("11011101xx111x", InstEmit.TldB, typeof(OpCodeTld)); Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4)); Set("1101111011111x", InstEmit.Tld4, typeof(OpCodeTld4B)); + Set("11011111011000", InstEmit.TmmlB, typeof(OpCodeTexture)); + Set("11011111010110", InstEmit.Tmml, typeof(OpCodeTexture)); Set("110111100x1110", InstEmit.Txd, typeof(OpCodeTxd)); Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex)); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 7f7a48a6..af5122be 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -716,6 +716,130 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + public static void TmmlB(EmitterContext context) + { + EmitTextureMipMapLevel(context, true); + } + + public static void Tmml(EmitterContext context) + { + EmitTextureMipMapLevel(context, false); + } + + private static void EmitTextureMipMapLevel(EmitterContext context, bool isBindless) + { + OpCodeTexture op = (OpCodeTexture)context.CurrOp; + + 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)); + } + + TextureFlags flags = TextureFlags.None; + + List sourcesList = new List(); + + if (isBindless) + { + sourcesList.Add(Rb()); + + flags |= TextureFlags.Bindless; + } + + SamplerType type = ConvertSamplerType(op.Dimensions); + + int coordsCount = type.GetDimensions(); + + Operand arrayIndex = op.IsArray ? Ra() : null; + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (op.IsArray) + { + sourcesList.Add(arrayIndex); + + type |= SamplerType.Array; + } + + 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(); + + // Components z and w aren't standard, we return 0 in this case and add a comment. + if (compIndex >= 2) + { + context.Add(new CommentNode("Unsupported component z or w found")); + context.Copy(dest, Const(0)); + } + else + { + Operand tempDest = Local(); + + TextureOperation operation = new TextureOperation( + Instruction.Lod, + type, + flags, + handle, + compIndex, + tempDest, + sources); + + context.Add(operation); + + tempDest = context.FPMultiply(tempDest, ConstF(256.0f)); + + Operand finalValue = context.FPConvertToS32(tempDest); + + context.Copy(dest, finalValue); + } + } + } + } + public static void Txd(EmitterContext context) { OpCodeTxd op = (OpCodeTxd)context.CurrOp;