aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs')
-rw-r--r--src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs1312
1 files changed, 1312 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
new file mode 100644
index 00000000..caa9a775
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
@@ -0,0 +1,1312 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ private static readonly int[,] _maskLut = new int[,]
+ {
+ { 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 },
+ { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 }
+ };
+
+ public const bool Sample1DAs2D = true;
+
+ private enum TexsType
+ {
+ Texs,
+ Tlds,
+ Tld4s
+ }
+
+ public static void Tex(EmitterContext context)
+ {
+ InstTex op = context.GetOp<InstTex>();
+
+ EmitTex(context, TextureFlags.None, op.Dim, op.Lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffi);
+ }
+
+ public static void TexB(EmitterContext context)
+ {
+ InstTexB op = context.GetOp<InstTexB>();
+
+ EmitTex(context, TextureFlags.Bindless, op.Dim, op.Lodb, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffib);
+ }
+
+ public static void Texs(EmitterContext context)
+ {
+ InstTexs op = context.GetOp<InstTexs>();
+
+ EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
+
+ public static void TexsF16(EmitterContext context)
+ {
+ InstTexs op = context.GetOp<InstTexs>();
+
+ EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
+ }
+
+ public static void Tld(EmitterContext context)
+ {
+ InstTld op = context.GetOp<InstTld>();
+
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ var lod = op.Lod ? Lod.Ll : Lod.Lz;
+
+ EmitTex(context, TextureFlags.IntCoords, op.Dim, lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
+ }
+
+ public static void TldB(EmitterContext context)
+ {
+ InstTldB op = context.GetOp<InstTldB>();
+
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ var flags = TextureFlags.IntCoords | TextureFlags.Bindless;
+ var lod = op.Lod ? Lod.Ll : Lod.Lz;
+
+ EmitTex(context, flags, op.Dim, lod, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
+ }
+
+ public static void Tlds(EmitterContext context)
+ {
+ InstTlds op = context.GetOp<InstTlds>();
+
+ EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
+
+ public static void TldsF16(EmitterContext context)
+ {
+ InstTlds op = context.GetOp<InstTlds>();
+
+ EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
+ }
+
+ public static void Tld4(EmitterContext context)
+ {
+ InstTld4 op = context.GetOp<InstTld4>();
+
+ EmitTld4(context, op.Dim, op.TexComp, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: false);
+ }
+
+ public static void Tld4B(EmitterContext context)
+ {
+ InstTld4B op = context.GetOp<InstTld4B>();
+
+ EmitTld4(context, op.Dim, op.TexComp, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: true);
+ }
+
+ public static void Tld4s(EmitterContext context)
+ {
+ InstTld4s op = context.GetOp<InstTld4s>();
+
+ EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
+
+ public static void Tld4sF16(EmitterContext context)
+ {
+ InstTld4s op = context.GetOp<InstTld4s>();
+
+ EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
+ }
+
+ public static void Tmml(EmitterContext context)
+ {
+ InstTmml op = context.GetOp<InstTmml>();
+
+ EmitTmml(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: false);
+ }
+
+ public static void TmmlB(EmitterContext context)
+ {
+ InstTmmlB op = context.GetOp<InstTmmlB>();
+
+ EmitTmml(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: true);
+ }
+
+ public static void Txd(EmitterContext context)
+ {
+ InstTxd op = context.GetOp<InstTxd>();
+
+ EmitTxd(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: false);
+ }
+
+ public static void TxdB(EmitterContext context)
+ {
+ InstTxdB op = context.GetOp<InstTxdB>();
+
+ EmitTxd(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: true);
+ }
+
+ public static void Txq(EmitterContext context)
+ {
+ InstTxq op = context.GetOp<InstTxq>();
+
+ EmitTxq(context, op.TexQuery, op.TidB, op.WMask, op.SrcA, op.Dest, isBindless: false);
+ }
+
+ public static void TxqB(EmitterContext context)
+ {
+ InstTxqB op = context.GetOp<InstTxqB>();
+
+ EmitTxq(context, op.TexQuery, 0, op.WMask, op.SrcA, op.Dest, isBindless: true);
+ }
+
+ private static void EmitTex(
+ EmitterContext context,
+ TextureFlags flags,
+ TexDim dimensions,
+ Lod lodMode,
+ int imm,
+ int componentMask,
+ int raIndex,
+ int rbIndex,
+ int rdIndex,
+ bool isMultisample,
+ bool hasDepthCompare,
+ bool hasOffset)
+ {
+ if (rdIndex == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ 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));
+ }
+
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ bool isArray = type.HasFlag(SamplerType.Array);
+ bool isBindless = flags.HasFlag(TextureFlags.Bindless);
+
+ Operand arrayIndex = isArray ? Ra() : null;
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(Rb());
+ }
+
+ bool hasLod = lodMode > Lod.Lz;
+
+ if (type == SamplerType.Texture1D && (flags & ~TextureFlags.Bindless) == TextureFlags.IntCoords && !(
+ hasLod ||
+ hasDepthCompare ||
+ hasOffset ||
+ isArray ||
+ isMultisample))
+ {
+ // For bindless, we don't have any way to know the texture type,
+ // so we assume it's texture buffer when the sampler type is 1D, since that's more common.
+ bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QuerySamplerType(imm) == SamplerType.TextureBuffer;
+ if (isTypeBuffer)
+ {
+ type = SamplerType.TextureBuffer;
+ }
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ bool is1DTo2D = false;
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
+ is1DTo2D = true;
+ }
+
+ if (isArray)
+ {
+ sourcesList.Add(arrayIndex);
+ }
+
+ Operand lodValue = hasLod ? Rb() : ConstF(0);
+
+ Operand packedOffs = hasOffset ? Rb() : null;
+
+ if (hasDepthCompare)
+ {
+ sourcesList.Add(Rb());
+
+ type |= SamplerType.Shadow;
+ }
+
+ if ((lodMode == Lod.Lz ||
+ lodMode == Lod.Ll ||
+ lodMode == Lod.Lla) && !isMultisample && type != SamplerType.TextureBuffer)
+ {
+ sourcesList.Add(lodValue);
+
+ flags |= TextureFlags.LodLevel;
+ }
+
+ if (hasOffset)
+ {
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
+ }
+
+ if (is1DTo2D)
+ {
+ sourcesList.Add(Const(0));
+ }
+
+ flags |= TextureFlags.Offset;
+ }
+
+ if (lodMode == Lod.Lb || lodMode == Lod.Lba)
+ {
+ sourcesList.Add(lodValue);
+
+ flags |= TextureFlags.LodBias;
+ }
+
+ if (isMultisample)
+ {
+ sourcesList.Add(Rb());
+
+ type |= SamplerType.Multisample;
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+ Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
+
+ int outputIndex = 0;
+
+ for (int i = 0; i < dests.Length; i++)
+ {
+ if (rdIndex + i >= RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ dests[outputIndex++] = Register(rdIndex + i, RegisterType.Gpr);
+ }
+
+ if (outputIndex != dests.Length)
+ {
+ Array.Resize(ref dests, outputIndex);
+ }
+
+ int handle = !isBindless ? imm : 0;
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.TextureSample,
+ type,
+ flags,
+ handle,
+ componentMask,
+ dests,
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static void EmitTexs(
+ EmitterContext context,
+ TexsType texsType,
+ int imm,
+ int writeMask,
+ int srcA,
+ int srcB,
+ int dest,
+ int dest2,
+ bool isF16)
+ {
+ if (dest == RegisterConsts.RegisterZeroIndex && dest2 == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, 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)));
+ }
+ }
+
+ SamplerType type;
+ TextureFlags flags;
+
+ if (texsType == TexsType.Texs)
+ {
+ var texsOp = context.GetOp<InstTexs>();
+
+ type = ConvertSamplerType(texsOp.Target);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid texture sampler type.");
+ return;
+ }
+
+ flags = ConvertTextureFlags(texsOp.Target);
+
+ // We don't need to handle 1D -> Buffer conversions here as
+ // only texture sample with integer coordinates can ever use buffer targets.
+
+ if ((type & SamplerType.Array) != 0)
+ {
+ Operand arrayIndex = Ra();
+
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+
+ sourcesList.Add(arrayIndex);
+
+ if ((type & SamplerType.Shadow) != 0)
+ {
+ sourcesList.Add(Rb());
+ }
+
+ if ((flags & TextureFlags.LodLevel) != 0)
+ {
+ sourcesList.Add(ConstF(0));
+ }
+ }
+ else
+ {
+ switch (texsOp.Target)
+ {
+ case TexsTarget.Texture1DLodZero:
+ sourcesList.Add(Ra());
+
+ if (Sample1DAs2D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ sourcesList.Add(ConstF(0));
+ break;
+
+ case TexsTarget.Texture2D:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ break;
+
+ case TexsTarget.Texture2DLodZero:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ sourcesList.Add(ConstF(0));
+ break;
+
+ case TexsTarget.Texture2DLodLevel:
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture3D:
+ case TexsTarget.TextureCube:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ break;
+
+ case TexsTarget.Texture2DLodZeroDepthCompare:
+ case TexsTarget.Texture3DLodZero:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ sourcesList.Add(ConstF(0));
+ break;
+
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.TextureCubeLodLevel:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ sourcesList.Add(Rb());
+ break;
+ }
+ }
+ }
+ else if (texsType == TexsType.Tlds)
+ {
+ var tldsOp = context.GetOp<InstTlds>();
+
+ type = ConvertSamplerType(tldsOp.Target);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid texel fetch sampler type.");
+ return;
+ }
+
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords;
+
+ if (tldsOp.Target == TldsTarget.Texture1DLodZero &&
+ context.Config.GpuAccessor.QuerySamplerType(tldsOp.TidB) == SamplerType.TextureBuffer)
+ {
+ type = SamplerType.TextureBuffer;
+ flags &= ~TextureFlags.LodLevel;
+ }
+
+ switch (tldsOp.Target)
+ {
+ case TldsTarget.Texture1DLodZero:
+ sourcesList.Add(Ra());
+
+ if (type != SamplerType.TextureBuffer)
+ {
+ if (Sample1DAs2D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ sourcesList.Add(ConstF(0));
+ }
+ break;
+
+ case TldsTarget.Texture1DLodLevel:
+ sourcesList.Add(Ra());
+
+ if (Sample1DAs2D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ sourcesList.Add(Rb());
+ break;
+
+ case TldsTarget.Texture2DLodZero:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ sourcesList.Add(Const(0));
+ break;
+
+ case TldsTarget.Texture2DLodZeroOffset:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Const(0));
+ break;
+
+ case TldsTarget.Texture2DLodZeroMultisample:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodLevelOffset:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ break;
+
+ case TldsTarget.Texture3DLodZero:
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ sourcesList.Add(Const(0));
+ break;
+
+ case TldsTarget.Texture2DArrayLodZero:
+ sourcesList.Add(Rb());
+ sourcesList.Add(Rb());
+ sourcesList.Add(Ra());
+ sourcesList.Add(Const(0));
+ break;
+ }
+
+ if ((flags & TextureFlags.Offset) != 0)
+ {
+ AddTextureOffset(type.GetDimensions(), 4, 4);
+ }
+ }
+ else if (texsType == TexsType.Tld4s)
+ {
+ var tld4sOp = context.GetOp<InstTld4s>();
+
+ if (!(tld4sOp.Dc || tld4sOp.Aoffi))
+ {
+ sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
+ }
+ else
+ {
+ sourcesList.Add(Ra());
+ sourcesList.Add(Ra());
+ }
+
+ type = SamplerType.Texture2D;
+ flags = TextureFlags.Gather;
+
+ if (tld4sOp.Dc)
+ {
+ sourcesList.Add(Rb());
+
+ type |= SamplerType.Shadow;
+ }
+
+ if (tld4sOp.Aoffi)
+ {
+ AddTextureOffset(type.GetDimensions(), 8, 6);
+
+ flags |= TextureFlags.Offset;
+ }
+
+ sourcesList.Add(Const((int)tld4sOp.TexComp));
+ }
+ else
+ {
+ throw new ArgumentException($"Invalid TEXS type \"{texsType}\".");
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+
+ Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
+ Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
+
+ int handle = imm;
+ int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
+
+ int componentsCount = BitOperations.PopCount((uint)componentMask);
+
+ Operand[] dests = new Operand[componentsCount];
+
+ int outputIndex = 0;
+
+ for (int i = 0; i < componentsCount; i++)
+ {
+ int high = i >> 1;
+ int low = i & 1;
+
+ if (isF16)
+ {
+ dests[outputIndex++] = high != 0
+ ? (rd1[low] = Local())
+ : (rd0[low] = Local());
+ }
+ else
+ {
+ int rdIndex = high != 0 ? dest2 : dest;
+
+ if (rdIndex < RegisterConsts.RegisterZeroIndex)
+ {
+ rdIndex += low;
+ }
+
+ dests[outputIndex++] = Register(rdIndex, RegisterType.Gpr);
+ }
+ }
+
+ if (outputIndex != dests.Length)
+ {
+ Array.Resize(ref dests, outputIndex);
+ }
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.TextureSample,
+ type,
+ flags,
+ handle,
+ componentMask,
+ dests,
+ sources);
+
+ context.Add(operation);
+
+ if (isF16)
+ {
+ context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1]));
+ context.Copy(Register(dest2, RegisterType.Gpr), context.PackHalf2x16(rd1[0], rd1[1]));
+ }
+ }
+
+ private static void EmitTld4(
+ EmitterContext context,
+ TexDim dimensions,
+ TexComp component,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ TexOffset offset,
+ bool hasDepthCompare,
+ bool isBindless)
+ {
+ if (dest == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ Operand arrayIndex = isArray ? Ra() : null;
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ SamplerType type = ConvertSamplerType(dimensions);
+ TextureFlags flags = TextureFlags.Gather;
+
+ if (isBindless)
+ {
+ sourcesList.Add(Rb());
+
+ flags |= TextureFlags.Bindless;
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
+
+ if (is1DTo2D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
+ }
+
+ if (isArray)
+ {
+ sourcesList.Add(arrayIndex);
+ }
+
+ Operand[] packedOffs = new Operand[2];
+
+ bool hasAnyOffset = offset == TexOffset.Aoffi || offset == TexOffset.Ptp;
+
+ packedOffs[0] = hasAnyOffset ? Rb() : null;
+ packedOffs[1] = offset == TexOffset.Ptp ? Rb() : null;
+
+ if (hasDepthCompare)
+ {
+ sourcesList.Add(Rb());
+
+ type |= SamplerType.Shadow;
+ }
+
+ if (hasAnyOffset)
+ {
+ int offsetTexelsCount = offset == TexOffset.Ptp ? 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)));
+ }
+
+ if (is1DTo2D)
+ {
+ for (int index = 0; index < offsetTexelsCount; index++)
+ {
+ sourcesList.Add(Const(0));
+ }
+ }
+
+ flags |= offset == TexOffset.Ptp ? TextureFlags.Offsets : TextureFlags.Offset;
+ }
+
+ sourcesList.Add(Const((int)component));
+
+ Operand[] sources = sourcesList.ToArray();
+ Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
+
+ int outputIndex = 0;
+
+ for (int i = 0; i < dests.Length; i++)
+ {
+ if (dest + i >= RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
+ }
+
+ if (outputIndex != dests.Length)
+ {
+ Array.Resize(ref dests, outputIndex);
+ }
+
+ int handle = imm;
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.TextureSample,
+ type,
+ flags,
+ handle,
+ componentMask,
+ dests,
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static void EmitTmml(
+ EmitterContext context,
+ TexDim dimensions,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ bool isBindless)
+ {
+ if (dest == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ TextureFlags flags = TextureFlags.None;
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(Rb());
+
+ flags |= TextureFlags.Bindless;
+ }
+
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ int coordsCount = type.GetDimensions();
+
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ Operand arrayIndex = isArray ? Ra() : null;
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
+ }
+
+ if (isArray)
+ {
+ sourcesList.Add(arrayIndex);
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+
+ Operand GetDest()
+ {
+ if (dest >= RegisterConsts.RegisterZeroIndex)
+ {
+ return null;
+ }
+
+ return Register(dest++, RegisterType.Gpr);
+ }
+
+ int handle = imm;
+
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ {
+ if ((compMask & 1) != 0)
+ {
+ Operand destOperand = GetDest();
+
+ if (destOperand == null)
+ {
+ break;
+ }
+
+ // 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(destOperand, Const(0));
+ }
+ else
+ {
+ Operand tempDest = Local();
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.Lod,
+ type,
+ flags,
+ handle,
+ compIndex ^ 1, // The instruction component order is the inverse of GLSL's.
+ new[] { tempDest },
+ sources);
+
+ context.Add(operation);
+
+ tempDest = context.FPMultiply(tempDest, ConstF(256.0f));
+
+ Operand fixedPointValue = context.FP32ConvertToS32(tempDest);
+
+ context.Copy(destOperand, fixedPointValue);
+ }
+ }
+ }
+ }
+
+ private static void EmitTxd(
+ EmitterContext context,
+ TexDim dimensions,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ bool hasOffset,
+ bool isBindless)
+ {
+ if (dest == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ TextureFlags flags = TextureFlags.Derivatives;
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(Ra());
+
+ flags |= TextureFlags.Bindless;
+ }
+
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
+
+ if (is1DTo2D)
+ {
+ sourcesList.Add(ConstF(0));
+
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
+ }
+
+ Operand packedParams = Ra();
+
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ if (isArray)
+ {
+ sourcesList.Add(context.BitwiseAnd(packedParams, Const(0xffff)));
+ }
+
+ // Derivatives (X and Y).
+ for (int dIndex = 0; dIndex < 2 * coordsCount; dIndex++)
+ {
+ sourcesList.Add(Rb());
+
+ if (is1DTo2D)
+ {
+ sourcesList.Add(ConstF(0));
+ }
+ }
+
+ if (hasOffset)
+ {
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(context.BitfieldExtractS32(packedParams, Const(16 + index * 4), Const(4)));
+ }
+
+ if (is1DTo2D)
+ {
+ sourcesList.Add(Const(0));
+ }
+
+ flags |= TextureFlags.Offset;
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+ Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
+
+ int outputIndex = 0;
+
+ for (int i = 0; i < dests.Length; i++)
+ {
+ if (dest + i >= RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
+ }
+
+ if (outputIndex != dests.Length)
+ {
+ Array.Resize(ref dests, outputIndex);
+ }
+
+ int handle = imm;
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.TextureSample,
+ type,
+ flags,
+ handle,
+ componentMask,
+ dests,
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static void EmitTxq(
+ EmitterContext context,
+ TexQuery query,
+ int imm,
+ int componentMask,
+ int srcA,
+ int dest,
+ bool isBindless)
+ {
+ if (dest == RegisterConsts.RegisterZeroIndex)
+ {
+ return;
+ }
+
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ // TODO: Validate and use query.
+ Instruction inst = Instruction.TextureSize;
+ TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ sourcesList.Add(Ra());
+
+ Operand[] sources = sourcesList.ToArray();
+
+ Operand GetDest()
+ {
+ if (dest >= RegisterConsts.RegisterZeroIndex)
+ {
+ return null;
+ }
+
+ return Register(dest++, RegisterType.Gpr);
+ }
+
+ SamplerType type;
+
+ if (isBindless)
+ {
+ type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
+ }
+ else
+ {
+ type = context.Config.GpuAccessor.QuerySamplerType(imm);
+ }
+
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ {
+ if ((compMask & 1) != 0)
+ {
+ Operand destOperand = GetDest();
+
+ if (destOperand == null)
+ {
+ break;
+ }
+
+ TextureOperation operation = context.CreateTextureOperation(
+ inst,
+ type,
+ flags,
+ imm,
+ compIndex,
+ new[] { destOperand },
+ sources);
+
+ context.Add(operation);
+ }
+ }
+ }
+
+ private static SamplerType ConvertSamplerType(TexDim dimensions)
+ {
+ return dimensions switch
+ {
+ TexDim._1d => SamplerType.Texture1D,
+ TexDim.Array1d => SamplerType.Texture1D | SamplerType.Array,
+ TexDim._2d => SamplerType.Texture2D,
+ TexDim.Array2d => SamplerType.Texture2D | SamplerType.Array,
+ TexDim._3d => SamplerType.Texture3D,
+ TexDim.Array3d => SamplerType.Texture3D | SamplerType.Array,
+ TexDim.Cube => SamplerType.TextureCube,
+ TexDim.ArrayCube => SamplerType.TextureCube | SamplerType.Array,
+ _ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".")
+ };
+ }
+
+ private static SamplerType ConvertSamplerType(TexsTarget type)
+ {
+ switch (type)
+ {
+ case TexsTarget.Texture1DLodZero:
+ return SamplerType.Texture1D;
+
+ case TexsTarget.Texture2D:
+ case TexsTarget.Texture2DLodZero:
+ case TexsTarget.Texture2DLodLevel:
+ return SamplerType.Texture2D;
+
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.Texture2DLodZeroDepthCompare:
+ return SamplerType.Texture2D | SamplerType.Shadow;
+
+ case TexsTarget.Texture2DArray:
+ case TexsTarget.Texture2DArrayLodZero:
+ return SamplerType.Texture2D | SamplerType.Array;
+
+ case TexsTarget.Texture2DArrayLodZeroDepthCompare:
+ return SamplerType.Texture2D | SamplerType.Array | SamplerType.Shadow;
+
+ case TexsTarget.Texture3D:
+ case TexsTarget.Texture3DLodZero:
+ return SamplerType.Texture3D;
+
+ case TexsTarget.TextureCube:
+ case TexsTarget.TextureCubeLodLevel:
+ return SamplerType.TextureCube;
+ }
+
+ return SamplerType.None;
+ }
+
+ private static SamplerType ConvertSamplerType(TldsTarget type)
+ {
+ switch (type)
+ {
+ case TldsTarget.Texture1DLodZero:
+ case TldsTarget.Texture1DLodLevel:
+ return SamplerType.Texture1D;
+
+ case TldsTarget.Texture2DLodZero:
+ case TldsTarget.Texture2DLodZeroOffset:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodLevelOffset:
+ return SamplerType.Texture2D;
+
+ case TldsTarget.Texture2DLodZeroMultisample:
+ return SamplerType.Texture2D | SamplerType.Multisample;
+
+ case TldsTarget.Texture3DLodZero:
+ return SamplerType.Texture3D;
+
+ case TldsTarget.Texture2DArrayLodZero:
+ return SamplerType.Texture2D | SamplerType.Array;
+ }
+
+ return SamplerType.None;
+ }
+
+ private static TextureFlags ConvertTextureFlags(TexsTarget type)
+ {
+ switch (type)
+ {
+ case TexsTarget.Texture1DLodZero:
+ case TexsTarget.Texture2DLodZero:
+ case TexsTarget.Texture2DLodLevel:
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.Texture2DLodZeroDepthCompare:
+ case TexsTarget.Texture2DArrayLodZero:
+ case TexsTarget.Texture2DArrayLodZeroDepthCompare:
+ case TexsTarget.Texture3DLodZero:
+ case TexsTarget.TextureCubeLodLevel:
+ return TextureFlags.LodLevel;
+
+ case TexsTarget.Texture2D:
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture2DArray:
+ case TexsTarget.Texture3D:
+ case TexsTarget.TextureCube:
+ return TextureFlags.None;
+ }
+
+ return TextureFlags.None;
+ }
+
+ private static TextureFlags ConvertTextureFlags(TldsTarget type)
+ {
+ switch (type)
+ {
+ case TldsTarget.Texture1DLodZero:
+ case TldsTarget.Texture1DLodLevel:
+ case TldsTarget.Texture2DLodZero:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodZeroMultisample:
+ case TldsTarget.Texture3DLodZero:
+ case TldsTarget.Texture2DArrayLodZero:
+ return TextureFlags.LodLevel;
+
+ case TldsTarget.Texture2DLodZeroOffset:
+ case TldsTarget.Texture2DLodLevelOffset:
+ return TextureFlags.LodLevel | TextureFlags.Offset;
+ }
+
+ return TextureFlags.None;
+ }
+ }
+} \ No newline at end of file