diff options
Diffstat (limited to 'Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs')
-rw-r--r-- | Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs | 776 |
1 files changed, 776 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs new file mode 100644 index 00000000..1b19d901 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs @@ -0,0 +1,776 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Tex(EmitterContext context) + { + Tex(context, TextureFlags.None); + } + + public static void Tex_B(EmitterContext context) + { + Tex(context, TextureFlags.Bindless); + } + + public static void Tld(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords); + } + + public static void Tld_B(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords | TextureFlags.Bindless); + } + + public static void Texs(EmitterContext context) + { + OpCodeTextureScalar op = (OpCodeTextureScalar)context.CurrOp; + + if (op.Rd0.IsRZ && op.Rd1.IsRZ) + { + return; + } + + List<Operand> sourcesList = new List<Operand>(); + + int raIndex = op.Ra.Index; + int rbIndex = op.Rb.Index; + + 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)); + } + + void AddTextureOffset(int coordsCount, int stride, int size) + { + Operand packedOffs = Rb(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * stride), Const(size))); + } + } + + TextureType type; + TextureFlags flags; + + if (op is OpCodeTexs texsOp) + { + type = GetTextureType (texsOp.Type); + flags = GetTextureFlags(texsOp.Type); + + if ((type & TextureType.Array) != 0) + { + 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 TextureScalarType.Texture1DLodZero: + sourcesList.Add(Ra()); + break; + + case TextureScalarType.Texture2D: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TextureScalarType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TextureScalarType.Texture2DLodLevel: + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture3D: + case TextureScalarType.TextureCube: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TextureScalarType.Texture2DLodZeroDepthCompare: + case TextureScalarType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.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 TexelLoadScalarType.Texture1DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture1DLodLevel: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexelLoadScalarType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DLodZeroOffset: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DLodZeroMultisample: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodLevelOffset: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexelLoadScalarType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DArrayLodZero: + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + } + + if ((flags & TextureFlags.Offset) != 0) + { + AddTextureOffset(type.GetCoordsCount(), 4, 4); + } + } + else if (op is OpCodeTld4s tld4sOp) + { + if (!(tld4sOp.HasDepthCompare || tld4sOp.HasOffset)) + { + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + } + else + { + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + } + + type = TextureType.Texture2D; + flags = TextureFlags.Gather; + + if (tld4sOp.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if (tld4sOp.HasOffset) + { + AddTextureOffset(type.GetCoordsCount(), 8, 6); + + flags |= TextureFlags.Offset; + } + + sourcesList.Add(Const(tld4sOp.GatherCompIndex)); + } + else + { + throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\"."); + } + + Operand[] sources = sourcesList.ToArray(); + + Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) }; + Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) }; + + int destIncrement = 0; + + Operand GetDest() + { + int high = destIncrement >> 1; + int low = destIncrement & 1; + + destIncrement++; + + if (op.IsFp16) + { + return high != 0 + ? (rd1[low] = Local()) + : (rd0[low] = Local()); + } + else + { + int rdIndex = high != 0 ? op.Rd1.Index : op.Rd0.Index; + + if (rdIndex < RegisterConsts.RegisterZeroIndex) + { + rdIndex += low; + } + + return Register(rdIndex, RegisterType.Gpr); + } + } + + int handle = 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, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + + if (op.IsFp16) + { + context.Copy(Register(op.Rd0), context.PackHalf2x16(rd0[0], rd0[1])); + context.Copy(Register(op.Rd1), context.PackHalf2x16(rd1[0], rd1[1])); + } + } + + public static void Tld4(EmitterContext context) + { + OpCodeTld4 op = (OpCodeTld4)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)); + } + + Operand arrayIndex = op.IsArray ? Ra() : null; + + List<Operand> sourcesList = new List<Operand>(); + + TextureType type = GetTextureType(op.Dimensions); + + TextureFlags flags = TextureFlags.Gather; + + int coordsCount = type.GetCoordsCount(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (op.IsArray) + { + sourcesList.Add(arrayIndex); + + type |= TextureType.Array; + } + + Operand[] packedOffs = new Operand[2]; + + packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null; + packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null; + + if (op.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if (op.Offset != TextureGatherOffset.None) + { + int offsetTexelsCount = op.Offset == TextureGatherOffset.Offsets ? 4 : 1; + + 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 |= op.Offset == TextureGatherOffset.Offsets + ? TextureFlags.Offsets + : TextureFlags.Offset; + } + + sourcesList.Add(Const(op.GatherCompIndex)); + + 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 = 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, + 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.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if ((op.LodMode == TextureLodMode.LodZero || + op.LodMode == TextureLodMode.LodLevel || + op.LodMode == TextureLodMode.LodLevelA) && !op.IsMultisample) + { + 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.IsMultisample) + { + sourcesList.Add(Rb()); + + type |= TextureType.Multisample; + } + + 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); + + context.Add(operation); + } + } + } + + private static TextureType GetTextureType(TextureDimensions dimensions) + { + switch (dimensions) + { + case TextureDimensions.Texture1D: return TextureType.Texture1D; + case TextureDimensions.Texture2D: return TextureType.Texture2D; + case TextureDimensions.Texture3D: return TextureType.Texture3D; + case TextureDimensions.TextureCube: return TextureType.TextureCube; + } + + throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."); + } + + private static TextureType GetTextureType(TextureScalarType type) + { + switch (type) + { + case TextureScalarType.Texture1DLodZero: + return TextureType.Texture1D; + + case TextureScalarType.Texture2D: + case TextureScalarType.Texture2DLodZero: + case TextureScalarType.Texture2DLodLevel: + return TextureType.Texture2D; + + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.Texture2DLodZeroDepthCompare: + return TextureType.Texture2D | TextureType.Shadow; + + case TextureScalarType.Texture2DArray: + case TextureScalarType.Texture2DArrayLodZero: + return TextureType.Texture2D | TextureType.Array; + + case TextureScalarType.Texture2DArrayLodZeroDepthCompare: + return TextureType.Texture2D | TextureType.Array | TextureType.Shadow; + + case TextureScalarType.Texture3D: + case TextureScalarType.Texture3DLodZero: + return TextureType.Texture3D; + + case TextureScalarType.TextureCube: + case TextureScalarType.TextureCubeLodLevel: + return TextureType.TextureCube; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureType GetTextureType(TexelLoadScalarType type) + { + switch (type) + { + case TexelLoadScalarType.Texture1DLodZero: + case TexelLoadScalarType.Texture1DLodLevel: + return TextureType.Texture1D; + + case TexelLoadScalarType.Texture2DLodZero: + case TexelLoadScalarType.Texture2DLodZeroOffset: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodLevelOffset: + return TextureType.Texture2D; + + case TexelLoadScalarType.Texture2DLodZeroMultisample: + return TextureType.Texture2D | TextureType.Multisample; + + case TexelLoadScalarType.Texture3DLodZero: + return TextureType.Texture3D; + + case TexelLoadScalarType.Texture2DArrayLodZero: + return TextureType.Texture2D | TextureType.Array; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureFlags GetTextureFlags(TextureScalarType type) + { + switch (type) + { + case TextureScalarType.Texture1DLodZero: + case TextureScalarType.Texture2DLodZero: + case TextureScalarType.Texture2DLodLevel: + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.Texture2DLodZeroDepthCompare: + case TextureScalarType.Texture2DArrayLodZero: + case TextureScalarType.Texture2DArrayLodZeroDepthCompare: + case TextureScalarType.Texture3DLodZero: + case TextureScalarType.TextureCubeLodLevel: + return TextureFlags.LodLevel; + + case TextureScalarType.Texture2D: + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture2DArray: + case TextureScalarType.Texture3D: + case TextureScalarType.TextureCube: + return TextureFlags.None; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureFlags GetTextureFlags(TexelLoadScalarType type) + { + switch (type) + { + case TexelLoadScalarType.Texture1DLodZero: + case TexelLoadScalarType.Texture1DLodLevel: + case TexelLoadScalarType.Texture2DLodZero: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodZeroMultisample: + case TexelLoadScalarType.Texture3DLodZero: + case TexelLoadScalarType.Texture2DArrayLodZero: + return TextureFlags.LodLevel; + + case TexelLoadScalarType.Texture2DLodZeroOffset: + case TexelLoadScalarType.Texture2DLodLevelOffset: + return TextureFlags.LodLevel | TextureFlags.Offset; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + } +}
\ No newline at end of file |