/ src / Ryujinx.Graphics.Shader / Instructions / InstEmitTexture.cs
InstEmitTexture.cs
   1  using Ryujinx.Graphics.Shader.Decoders;
   2  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
   3  using Ryujinx.Graphics.Shader.Translation;
   4  using System;
   5  using System.Collections.Generic;
   6  using System.Numerics;
   7  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
   8  
   9  namespace Ryujinx.Graphics.Shader.Instructions
  10  {
  11      static partial class InstEmit
  12      {
  13          private static readonly int[][] _maskLut = new int[][]
  14          {
  15              new int[] { 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 },
  16              new int[] { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 },
  17          };
  18  
  19          public const bool Sample1DAs2D = true;
  20  
  21          private enum TexsType
  22          {
  23              Texs,
  24              Tlds,
  25              Tld4s,
  26          }
  27  
  28          public static void Tex(EmitterContext context)
  29          {
  30              InstTex op = context.GetOp<InstTex>();
  31  
  32              EmitTex(context, TextureFlags.None, op.Dim, op.Lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffi);
  33          }
  34  
  35          public static void TexB(EmitterContext context)
  36          {
  37              InstTexB op = context.GetOp<InstTexB>();
  38  
  39              EmitTex(context, TextureFlags.Bindless, op.Dim, op.Lodb, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffib);
  40          }
  41  
  42          public static void Texs(EmitterContext context)
  43          {
  44              InstTexs op = context.GetOp<InstTexs>();
  45  
  46              EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
  47          }
  48  
  49          public static void TexsF16(EmitterContext context)
  50          {
  51              InstTexs op = context.GetOp<InstTexs>();
  52  
  53              EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
  54          }
  55  
  56          public static void Tld(EmitterContext context)
  57          {
  58              InstTld op = context.GetOp<InstTld>();
  59  
  60              var lod = op.Lod ? Lod.Ll : Lod.Lz;
  61  
  62              EmitTex(context, TextureFlags.IntCoords, op.Dim, lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
  63          }
  64  
  65          public static void TldB(EmitterContext context)
  66          {
  67              InstTldB op = context.GetOp<InstTldB>();
  68  
  69              var flags = TextureFlags.IntCoords | TextureFlags.Bindless;
  70              var lod = op.Lod ? Lod.Ll : Lod.Lz;
  71  
  72              EmitTex(context, flags, op.Dim, lod, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
  73          }
  74  
  75          public static void Tlds(EmitterContext context)
  76          {
  77              InstTlds op = context.GetOp<InstTlds>();
  78  
  79              EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
  80          }
  81  
  82          public static void TldsF16(EmitterContext context)
  83          {
  84              InstTlds op = context.GetOp<InstTlds>();
  85  
  86              EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
  87          }
  88  
  89          public static void Tld4(EmitterContext context)
  90          {
  91              InstTld4 op = context.GetOp<InstTld4>();
  92  
  93              EmitTld4(context, op.Dim, op.TexComp, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: false);
  94          }
  95  
  96          public static void Tld4B(EmitterContext context)
  97          {
  98              InstTld4B op = context.GetOp<InstTld4B>();
  99  
 100              EmitTld4(context, op.Dim, op.TexComp, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: true);
 101          }
 102  
 103          public static void Tld4s(EmitterContext context)
 104          {
 105              InstTld4s op = context.GetOp<InstTld4s>();
 106  
 107              EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
 108          }
 109  
 110          public static void Tld4sF16(EmitterContext context)
 111          {
 112              InstTld4s op = context.GetOp<InstTld4s>();
 113  
 114              EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
 115          }
 116  
 117          public static void Tmml(EmitterContext context)
 118          {
 119              InstTmml op = context.GetOp<InstTmml>();
 120  
 121              EmitTmml(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: false);
 122          }
 123  
 124          public static void TmmlB(EmitterContext context)
 125          {
 126              InstTmmlB op = context.GetOp<InstTmmlB>();
 127  
 128              EmitTmml(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: true);
 129          }
 130  
 131          public static void Txd(EmitterContext context)
 132          {
 133              InstTxd op = context.GetOp<InstTxd>();
 134  
 135              EmitTxd(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: false);
 136          }
 137  
 138          public static void TxdB(EmitterContext context)
 139          {
 140              InstTxdB op = context.GetOp<InstTxdB>();
 141  
 142              EmitTxd(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: true);
 143          }
 144  
 145          public static void Txq(EmitterContext context)
 146          {
 147              InstTxq op = context.GetOp<InstTxq>();
 148  
 149              EmitTxq(context, op.TexQuery, op.TidB, op.WMask, op.SrcA, op.Dest, isBindless: false);
 150          }
 151  
 152          public static void TxqB(EmitterContext context)
 153          {
 154              InstTxqB op = context.GetOp<InstTxqB>();
 155  
 156              EmitTxq(context, op.TexQuery, 0, op.WMask, op.SrcA, op.Dest, isBindless: true);
 157          }
 158  
 159          private static void EmitTex(
 160              EmitterContext context,
 161              TextureFlags flags,
 162              TexDim dimensions,
 163              Lod lodMode,
 164              int imm,
 165              int componentMask,
 166              int raIndex,
 167              int rbIndex,
 168              int rdIndex,
 169              bool isMultisample,
 170              bool hasDepthCompare,
 171              bool hasOffset)
 172          {
 173              if (rdIndex == RegisterConsts.RegisterZeroIndex)
 174              {
 175                  return;
 176              }
 177  
 178              Operand Ra()
 179              {
 180                  if (raIndex > RegisterConsts.RegisterZeroIndex)
 181                  {
 182                      return Const(0);
 183                  }
 184  
 185                  return context.Copy(Register(raIndex++, RegisterType.Gpr));
 186              }
 187  
 188              Operand Rb()
 189              {
 190                  if (rbIndex > RegisterConsts.RegisterZeroIndex)
 191                  {
 192                      return Const(0);
 193                  }
 194  
 195                  return context.Copy(Register(rbIndex++, RegisterType.Gpr));
 196              }
 197  
 198              SamplerType type = ConvertSamplerType(dimensions);
 199  
 200              bool isArray = type.HasFlag(SamplerType.Array);
 201              bool isBindless = flags.HasFlag(TextureFlags.Bindless);
 202  
 203              Operand arrayIndex = isArray ? Ra() : null;
 204  
 205              List<Operand> sourcesList = new();
 206  
 207              if (isBindless)
 208              {
 209                  sourcesList.Add(Rb());
 210              }
 211  
 212              bool hasLod = lodMode > Lod.Lz;
 213  
 214              if (type == SamplerType.Texture1D && (flags & ~TextureFlags.Bindless) == TextureFlags.IntCoords && !(
 215                  hasLod ||
 216                  hasDepthCompare ||
 217                  hasOffset ||
 218                  isArray ||
 219                  isMultisample))
 220              {
 221                  // For bindless, we don't have any way to know the texture type,
 222                  // so we assume it's texture buffer when the sampler type is 1D, since that's more common.
 223                  bool isTypeBuffer = isBindless || context.TranslatorContext.GpuAccessor.QuerySamplerType(imm) == SamplerType.TextureBuffer;
 224                  if (isTypeBuffer)
 225                  {
 226                      type = SamplerType.TextureBuffer;
 227                  }
 228              }
 229  
 230              int coordsCount = type.GetDimensions();
 231  
 232              for (int index = 0; index < coordsCount; index++)
 233              {
 234                  sourcesList.Add(Ra());
 235              }
 236  
 237              bool is1DTo2D = false;
 238  
 239              if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
 240              {
 241                  sourcesList.Add(ConstF(0));
 242  
 243                  type = SamplerType.Texture2D | (type & SamplerType.Array);
 244                  is1DTo2D = true;
 245              }
 246  
 247              if (isArray)
 248              {
 249                  sourcesList.Add(arrayIndex);
 250              }
 251  
 252              Operand lodValue = hasLod ? Rb() : ConstF(0);
 253  
 254              Operand packedOffs = hasOffset ? Rb() : null;
 255  
 256              if (hasDepthCompare)
 257              {
 258                  sourcesList.Add(Rb());
 259  
 260                  type |= SamplerType.Shadow;
 261              }
 262  
 263              if ((lodMode == Lod.Lz ||
 264                   lodMode == Lod.Ll ||
 265                   lodMode == Lod.Lla) && !isMultisample && type != SamplerType.TextureBuffer)
 266              {
 267                  sourcesList.Add(lodValue);
 268  
 269                  flags |= TextureFlags.LodLevel;
 270              }
 271  
 272              if (hasOffset)
 273              {
 274                  for (int index = 0; index < coordsCount; index++)
 275                  {
 276                      sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
 277                  }
 278  
 279                  if (is1DTo2D)
 280                  {
 281                      sourcesList.Add(Const(0));
 282                  }
 283  
 284                  flags |= TextureFlags.Offset;
 285              }
 286  
 287              if (lodMode == Lod.Lb || lodMode == Lod.Lba)
 288              {
 289                  sourcesList.Add(lodValue);
 290  
 291                  flags |= TextureFlags.LodBias;
 292              }
 293  
 294              if (isMultisample)
 295              {
 296                  sourcesList.Add(Rb());
 297  
 298                  type |= SamplerType.Multisample;
 299              }
 300  
 301              Operand[] sources = sourcesList.ToArray();
 302              Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
 303  
 304              int outputIndex = 0;
 305  
 306              for (int i = 0; i < dests.Length; i++)
 307              {
 308                  if (rdIndex + i >= RegisterConsts.RegisterZeroIndex)
 309                  {
 310                      break;
 311                  }
 312  
 313                  dests[outputIndex++] = Register(rdIndex + i, RegisterType.Gpr);
 314              }
 315  
 316              if (outputIndex != dests.Length)
 317              {
 318                  Array.Resize(ref dests, outputIndex);
 319              }
 320  
 321              int handle = !isBindless ? imm : 0;
 322  
 323              EmitTextureSample(context, type, flags, handle, componentMask, dests, sources);
 324          }
 325  
 326          private static void EmitTexs(
 327              EmitterContext context,
 328              TexsType texsType,
 329              int imm,
 330              int writeMask,
 331              int srcA,
 332              int srcB,
 333              int dest,
 334              int dest2,
 335              bool isF16)
 336          {
 337              if (dest == RegisterConsts.RegisterZeroIndex && dest2 == RegisterConsts.RegisterZeroIndex)
 338              {
 339                  return;
 340              }
 341  
 342              List<Operand> sourcesList = new();
 343  
 344              Operand Ra()
 345              {
 346                  if (srcA > RegisterConsts.RegisterZeroIndex)
 347                  {
 348                      return Const(0);
 349                  }
 350  
 351                  return context.Copy(Register(srcA++, RegisterType.Gpr));
 352              }
 353  
 354              Operand Rb()
 355              {
 356                  if (srcB > RegisterConsts.RegisterZeroIndex)
 357                  {
 358                      return Const(0);
 359                  }
 360  
 361                  return context.Copy(Register(srcB++, RegisterType.Gpr));
 362              }
 363  
 364              void AddTextureOffset(int coordsCount, int stride, int size)
 365              {
 366                  Operand packedOffs = Rb();
 367  
 368                  for (int index = 0; index < coordsCount; index++)
 369                  {
 370                      sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * stride), Const(size)));
 371                  }
 372              }
 373  
 374              SamplerType type;
 375              TextureFlags flags;
 376  
 377              if (texsType == TexsType.Texs)
 378              {
 379                  var texsOp = context.GetOp<InstTexs>();
 380  
 381                  type = ConvertSamplerType(texsOp.Target);
 382  
 383                  if (type == SamplerType.None)
 384                  {
 385                      context.TranslatorContext.GpuAccessor.Log("Invalid texture sampler type.");
 386                      return;
 387                  }
 388  
 389                  flags = ConvertTextureFlags(texsOp.Target);
 390  
 391                  // We don't need to handle 1D -> Buffer conversions here as
 392                  // only texture sample with integer coordinates can ever use buffer targets.
 393  
 394                  if ((type & SamplerType.Array) != 0)
 395                  {
 396                      Operand arrayIndex = Ra();
 397  
 398                      sourcesList.Add(Ra());
 399                      sourcesList.Add(Rb());
 400  
 401                      sourcesList.Add(arrayIndex);
 402  
 403                      if ((type & SamplerType.Shadow) != 0)
 404                      {
 405                          sourcesList.Add(Rb());
 406                      }
 407  
 408                      if ((flags & TextureFlags.LodLevel) != 0)
 409                      {
 410                          sourcesList.Add(ConstF(0));
 411                      }
 412                  }
 413                  else
 414                  {
 415                      switch (texsOp.Target)
 416                      {
 417                          case TexsTarget.Texture1DLodZero:
 418                              sourcesList.Add(Ra());
 419  
 420                              if (Sample1DAs2D)
 421                              {
 422                                  sourcesList.Add(ConstF(0));
 423  
 424                                  type &= ~SamplerType.Mask;
 425                                  type |= SamplerType.Texture2D;
 426                              }
 427  
 428                              sourcesList.Add(ConstF(0));
 429                              break;
 430  
 431                          case TexsTarget.Texture2D:
 432                              sourcesList.Add(Ra());
 433                              sourcesList.Add(Rb());
 434                              break;
 435  
 436                          case TexsTarget.Texture2DLodZero:
 437                              sourcesList.Add(Ra());
 438                              sourcesList.Add(Rb());
 439                              sourcesList.Add(ConstF(0));
 440                              break;
 441  
 442                          case TexsTarget.Texture2DLodLevel:
 443                          case TexsTarget.Texture2DDepthCompare:
 444                          case TexsTarget.Texture3D:
 445                          case TexsTarget.TextureCube:
 446                              sourcesList.Add(Ra());
 447                              sourcesList.Add(Ra());
 448                              sourcesList.Add(Rb());
 449                              break;
 450  
 451                          case TexsTarget.Texture2DLodZeroDepthCompare:
 452                          case TexsTarget.Texture3DLodZero:
 453                              sourcesList.Add(Ra());
 454                              sourcesList.Add(Ra());
 455                              sourcesList.Add(Rb());
 456                              sourcesList.Add(ConstF(0));
 457                              break;
 458  
 459                          case TexsTarget.Texture2DLodLevelDepthCompare:
 460                          case TexsTarget.TextureCubeLodLevel:
 461                              sourcesList.Add(Ra());
 462                              sourcesList.Add(Ra());
 463                              sourcesList.Add(Rb());
 464                              sourcesList.Add(Rb());
 465                              break;
 466                      }
 467                  }
 468              }
 469              else if (texsType == TexsType.Tlds)
 470              {
 471                  var tldsOp = context.GetOp<InstTlds>();
 472  
 473                  type = ConvertSamplerType(tldsOp.Target);
 474  
 475                  if (type == SamplerType.None)
 476                  {
 477                      context.TranslatorContext.GpuAccessor.Log("Invalid texel fetch sampler type.");
 478                      return;
 479                  }
 480  
 481                  flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords;
 482  
 483                  if (tldsOp.Target == TldsTarget.Texture1DLodZero &&
 484                      context.TranslatorContext.GpuAccessor.QuerySamplerType(tldsOp.TidB) == SamplerType.TextureBuffer)
 485                  {
 486                      type = SamplerType.TextureBuffer;
 487                      flags &= ~TextureFlags.LodLevel;
 488                  }
 489  
 490                  switch (tldsOp.Target)
 491                  {
 492                      case TldsTarget.Texture1DLodZero:
 493                          sourcesList.Add(Ra());
 494  
 495                          if (type != SamplerType.TextureBuffer)
 496                          {
 497                              if (Sample1DAs2D)
 498                              {
 499                                  sourcesList.Add(ConstF(0));
 500  
 501                                  type &= ~SamplerType.Mask;
 502                                  type |= SamplerType.Texture2D;
 503                              }
 504  
 505                              sourcesList.Add(ConstF(0));
 506                          }
 507                          break;
 508  
 509                      case TldsTarget.Texture1DLodLevel:
 510                          sourcesList.Add(Ra());
 511  
 512                          if (Sample1DAs2D)
 513                          {
 514                              sourcesList.Add(ConstF(0));
 515  
 516                              type &= ~SamplerType.Mask;
 517                              type |= SamplerType.Texture2D;
 518                          }
 519  
 520                          sourcesList.Add(Rb());
 521                          break;
 522  
 523                      case TldsTarget.Texture2DLodZero:
 524                          sourcesList.Add(Ra());
 525                          sourcesList.Add(Rb());
 526                          sourcesList.Add(Const(0));
 527                          break;
 528  
 529                      case TldsTarget.Texture2DLodZeroOffset:
 530                          sourcesList.Add(Ra());
 531                          sourcesList.Add(Ra());
 532                          sourcesList.Add(Const(0));
 533                          break;
 534  
 535                      case TldsTarget.Texture2DLodZeroMultisample:
 536                      case TldsTarget.Texture2DLodLevel:
 537                      case TldsTarget.Texture2DLodLevelOffset:
 538                          sourcesList.Add(Ra());
 539                          sourcesList.Add(Ra());
 540                          sourcesList.Add(Rb());
 541                          break;
 542  
 543                      case TldsTarget.Texture3DLodZero:
 544                          sourcesList.Add(Ra());
 545                          sourcesList.Add(Ra());
 546                          sourcesList.Add(Rb());
 547                          sourcesList.Add(Const(0));
 548                          break;
 549  
 550                      case TldsTarget.Texture2DArrayLodZero:
 551                          sourcesList.Add(Rb());
 552                          sourcesList.Add(Rb());
 553                          sourcesList.Add(Ra());
 554                          sourcesList.Add(Const(0));
 555                          break;
 556                  }
 557  
 558                  if ((flags & TextureFlags.Offset) != 0)
 559                  {
 560                      AddTextureOffset(type.GetDimensions(), 4, 4);
 561                  }
 562              }
 563              else if (texsType == TexsType.Tld4s)
 564              {
 565                  var tld4sOp = context.GetOp<InstTld4s>();
 566  
 567                  if (!(tld4sOp.Dc || tld4sOp.Aoffi))
 568                  {
 569                      sourcesList.Add(Ra());
 570                      sourcesList.Add(Rb());
 571                  }
 572                  else
 573                  {
 574                      sourcesList.Add(Ra());
 575                      sourcesList.Add(Ra());
 576                  }
 577  
 578                  type = SamplerType.Texture2D;
 579                  flags = TextureFlags.Gather;
 580  
 581                  int depthCompareIndex = sourcesList.Count;
 582  
 583                  if (tld4sOp.Aoffi)
 584                  {
 585                      AddTextureOffset(type.GetDimensions(), 8, 6);
 586  
 587                      flags |= TextureFlags.Offset;
 588                  }
 589  
 590                  if (tld4sOp.Dc)
 591                  {
 592                      sourcesList.Insert(depthCompareIndex, Rb());
 593  
 594                      type |= SamplerType.Shadow;
 595                  }
 596                  else
 597                  {
 598                      sourcesList.Add(Const((int)tld4sOp.TexComp));
 599                  }
 600              }
 601              else
 602              {
 603                  throw new ArgumentException($"Invalid TEXS type \"{texsType}\".");
 604              }
 605  
 606              Operand[] sources = sourcesList.ToArray();
 607  
 608              Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
 609              Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
 610  
 611              int handle = imm;
 612              int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1][writeMask];
 613  
 614              int componentsCount = BitOperations.PopCount((uint)componentMask);
 615  
 616              Operand[] dests = new Operand[componentsCount];
 617  
 618              int outputIndex = 0;
 619  
 620              for (int i = 0; i < componentsCount; i++)
 621              {
 622                  int high = i >> 1;
 623                  int low = i & 1;
 624  
 625                  if (isF16)
 626                  {
 627                      dests[outputIndex++] = high != 0
 628                          ? (rd1[low] = Local())
 629                          : (rd0[low] = Local());
 630                  }
 631                  else
 632                  {
 633                      int rdIndex = high != 0 ? dest2 : dest;
 634  
 635                      if (rdIndex < RegisterConsts.RegisterZeroIndex)
 636                      {
 637                          rdIndex += low;
 638                      }
 639  
 640                      dests[outputIndex++] = Register(rdIndex, RegisterType.Gpr);
 641                  }
 642              }
 643  
 644              if (outputIndex != dests.Length)
 645              {
 646                  Array.Resize(ref dests, outputIndex);
 647              }
 648  
 649              EmitTextureSample(context, type, flags, handle, componentMask, dests, sources);
 650  
 651              if (isF16)
 652              {
 653                  context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1]));
 654                  context.Copy(Register(dest2, RegisterType.Gpr), context.PackHalf2x16(rd1[0], rd1[1]));
 655              }
 656          }
 657  
 658          private static void EmitTld4(
 659              EmitterContext context,
 660              TexDim dimensions,
 661              TexComp component,
 662              int imm,
 663              int componentMask,
 664              int srcA,
 665              int srcB,
 666              int dest,
 667              TexOffset offset,
 668              bool hasDepthCompare,
 669              bool isBindless)
 670          {
 671              if (dest == RegisterConsts.RegisterZeroIndex)
 672              {
 673                  return;
 674              }
 675  
 676              Operand Ra()
 677              {
 678                  if (srcA > RegisterConsts.RegisterZeroIndex)
 679                  {
 680                      return Const(0);
 681                  }
 682  
 683                  return context.Copy(Register(srcA++, RegisterType.Gpr));
 684              }
 685  
 686              Operand Rb()
 687              {
 688                  if (srcB > RegisterConsts.RegisterZeroIndex)
 689                  {
 690                      return Const(0);
 691                  }
 692  
 693                  return context.Copy(Register(srcB++, RegisterType.Gpr));
 694              }
 695  
 696              bool isArray =
 697                  dimensions == TexDim.Array1d ||
 698                  dimensions == TexDim.Array2d ||
 699                  dimensions == TexDim.Array3d ||
 700                  dimensions == TexDim.ArrayCube;
 701  
 702              Operand arrayIndex = isArray ? Ra() : null;
 703  
 704              List<Operand> sourcesList = new();
 705  
 706              SamplerType type = ConvertSamplerType(dimensions);
 707              TextureFlags flags = TextureFlags.Gather;
 708  
 709              if (isBindless)
 710              {
 711                  sourcesList.Add(Rb());
 712  
 713                  flags |= TextureFlags.Bindless;
 714              }
 715  
 716              int coordsCount = type.GetDimensions();
 717  
 718              for (int index = 0; index < coordsCount; index++)
 719              {
 720                  sourcesList.Add(Ra());
 721              }
 722  
 723              bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
 724  
 725              if (is1DTo2D)
 726              {
 727                  sourcesList.Add(ConstF(0));
 728  
 729                  type = SamplerType.Texture2D | (type & SamplerType.Array);
 730              }
 731  
 732              if (isArray)
 733              {
 734                  sourcesList.Add(arrayIndex);
 735              }
 736  
 737              Operand[] packedOffs = new Operand[2];
 738  
 739              bool hasAnyOffset = offset == TexOffset.Aoffi || offset == TexOffset.Ptp;
 740  
 741              packedOffs[0] = hasAnyOffset ? Rb() : null;
 742              packedOffs[1] = offset == TexOffset.Ptp ? Rb() : null;
 743  
 744              if (hasDepthCompare)
 745              {
 746                  sourcesList.Add(Rb());
 747  
 748                  type |= SamplerType.Shadow;
 749              }
 750  
 751              if (hasAnyOffset)
 752              {
 753                  int offsetTexelsCount = offset == TexOffset.Ptp ? 4 : 1;
 754  
 755                  for (int index = 0; index < coordsCount * offsetTexelsCount; index++)
 756                  {
 757                      Operand packed = packedOffs[(index >> 2) & 1];
 758  
 759                      sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6)));
 760                  }
 761  
 762                  if (is1DTo2D)
 763                  {
 764                      for (int index = 0; index < offsetTexelsCount; index++)
 765                      {
 766                          sourcesList.Add(Const(0));
 767                      }
 768                  }
 769  
 770                  flags |= offset == TexOffset.Ptp ? TextureFlags.Offsets : TextureFlags.Offset;
 771              }
 772  
 773              if (!hasDepthCompare)
 774              {
 775                  sourcesList.Add(Const((int)component));
 776              }
 777  
 778              Operand[] sources = sourcesList.ToArray();
 779              Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
 780  
 781              int outputIndex = 0;
 782  
 783              for (int i = 0; i < dests.Length; i++)
 784              {
 785                  if (dest + i >= RegisterConsts.RegisterZeroIndex)
 786                  {
 787                      break;
 788                  }
 789  
 790                  dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
 791              }
 792  
 793              if (outputIndex != dests.Length)
 794              {
 795                  Array.Resize(ref dests, outputIndex);
 796              }
 797  
 798              EmitTextureSample(context, type, flags, imm, componentMask, dests, sources);
 799          }
 800  
 801          private static void EmitTmml(
 802              EmitterContext context,
 803              TexDim dimensions,
 804              int imm,
 805              int componentMask,
 806              int srcA,
 807              int srcB,
 808              int dest,
 809              bool isBindless)
 810          {
 811              if (dest == RegisterConsts.RegisterZeroIndex)
 812              {
 813                  return;
 814              }
 815  
 816              Operand Ra()
 817              {
 818                  if (srcA > RegisterConsts.RegisterZeroIndex)
 819                  {
 820                      return Const(0);
 821                  }
 822  
 823                  return context.Copy(Register(srcA++, RegisterType.Gpr));
 824              }
 825  
 826              Operand Rb()
 827              {
 828                  if (srcB > RegisterConsts.RegisterZeroIndex)
 829                  {
 830                      return Const(0);
 831                  }
 832  
 833                  return context.Copy(Register(srcB++, RegisterType.Gpr));
 834              }
 835  
 836              TextureFlags flags = TextureFlags.None;
 837  
 838              List<Operand> sourcesList = new();
 839  
 840              if (isBindless)
 841              {
 842                  sourcesList.Add(Rb());
 843  
 844                  flags |= TextureFlags.Bindless;
 845              }
 846  
 847              SamplerType type = ConvertSamplerType(dimensions);
 848  
 849              int coordsCount = type.GetDimensions();
 850  
 851              bool isArray =
 852                  dimensions == TexDim.Array1d ||
 853                  dimensions == TexDim.Array2d ||
 854                  dimensions == TexDim.Array3d ||
 855                  dimensions == TexDim.ArrayCube;
 856  
 857              Operand arrayIndex = isArray ? Ra() : null;
 858  
 859              for (int index = 0; index < coordsCount; index++)
 860              {
 861                  sourcesList.Add(Ra());
 862              }
 863  
 864              if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
 865              {
 866                  sourcesList.Add(ConstF(0));
 867  
 868                  type = SamplerType.Texture2D | (type & SamplerType.Array);
 869              }
 870  
 871              if (isArray)
 872              {
 873                  sourcesList.Add(arrayIndex);
 874              }
 875  
 876              Operand[] sources = sourcesList.ToArray();
 877  
 878              Operand GetDest()
 879              {
 880                  if (dest >= RegisterConsts.RegisterZeroIndex)
 881                  {
 882                      return null;
 883                  }
 884  
 885                  return Register(dest++, RegisterType.Gpr);
 886              }
 887  
 888              SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
 889                  Instruction.Lod,
 890                  type,
 891                  TextureFormat.Unknown,
 892                  flags,
 893                  TextureOperation.DefaultCbufSlot,
 894                  imm);
 895  
 896              for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
 897              {
 898                  if ((compMask & 1) != 0)
 899                  {
 900                      Operand d = GetDest();
 901  
 902                      if (d == null)
 903                      {
 904                          break;
 905                      }
 906  
 907                      // Components z and w aren't standard, we return 0 in this case and add a comment.
 908                      if (compIndex >= 2)
 909                      {
 910                          context.Add(new CommentNode("Unsupported component z or w found"));
 911                          context.Copy(d, Const(0));
 912                      }
 913                      else
 914                      {
 915                          // The instruction component order is the inverse of GLSL's.
 916                          Operand res = context.Lod(type, flags, setAndBinding, compIndex ^ 1, sources);
 917  
 918                          res = context.FPMultiply(res, ConstF(256.0f));
 919  
 920                          Operand fixedPointValue = context.FP32ConvertToS32(res);
 921  
 922                          context.Copy(d, fixedPointValue);
 923                      }
 924                  }
 925              }
 926          }
 927  
 928          private static void EmitTxd(
 929              EmitterContext context,
 930              TexDim dimensions,
 931              int imm,
 932              int componentMask,
 933              int srcA,
 934              int srcB,
 935              int dest,
 936              bool hasOffset,
 937              bool isBindless)
 938          {
 939              if (dest == RegisterConsts.RegisterZeroIndex)
 940              {
 941                  return;
 942              }
 943  
 944              Operand Ra()
 945              {
 946                  if (srcA > RegisterConsts.RegisterZeroIndex)
 947                  {
 948                      return Const(0);
 949                  }
 950  
 951                  return context.Copy(Register(srcA++, RegisterType.Gpr));
 952              }
 953  
 954              Operand Rb()
 955              {
 956                  if (srcB > RegisterConsts.RegisterZeroIndex)
 957                  {
 958                      return Const(0);
 959                  }
 960  
 961                  return context.Copy(Register(srcB++, RegisterType.Gpr));
 962              }
 963  
 964              TextureFlags flags = TextureFlags.Derivatives;
 965  
 966              List<Operand> sourcesList = new();
 967  
 968              if (isBindless)
 969              {
 970                  sourcesList.Add(Ra());
 971  
 972                  flags |= TextureFlags.Bindless;
 973              }
 974  
 975              SamplerType type = ConvertSamplerType(dimensions);
 976  
 977              int coordsCount = type.GetDimensions();
 978  
 979              for (int index = 0; index < coordsCount; index++)
 980              {
 981                  sourcesList.Add(Ra());
 982              }
 983  
 984              bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
 985  
 986              if (is1DTo2D)
 987              {
 988                  sourcesList.Add(ConstF(0));
 989  
 990                  type = SamplerType.Texture2D | (type & SamplerType.Array);
 991              }
 992  
 993              Operand packedParams = Ra();
 994  
 995              bool isArray =
 996                  dimensions == TexDim.Array1d ||
 997                  dimensions == TexDim.Array2d ||
 998                  dimensions == TexDim.Array3d ||
 999                  dimensions == TexDim.ArrayCube;
1000  
1001              if (isArray)
1002              {
1003                  sourcesList.Add(context.BitwiseAnd(packedParams, Const(0xffff)));
1004              }
1005  
1006              // Derivatives (X and Y).
1007              for (int dIndex = 0; dIndex < 2 * coordsCount; dIndex++)
1008              {
1009                  sourcesList.Add(Rb());
1010  
1011                  if (is1DTo2D)
1012                  {
1013                      sourcesList.Add(ConstF(0));
1014                  }
1015              }
1016  
1017              if (hasOffset)
1018              {
1019                  for (int index = 0; index < coordsCount; index++)
1020                  {
1021                      sourcesList.Add(context.BitfieldExtractS32(packedParams, Const(16 + index * 4), Const(4)));
1022                  }
1023  
1024                  if (is1DTo2D)
1025                  {
1026                      sourcesList.Add(Const(0));
1027                  }
1028  
1029                  flags |= TextureFlags.Offset;
1030              }
1031  
1032              Operand[] sources = sourcesList.ToArray();
1033              Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)];
1034  
1035              int outputIndex = 0;
1036  
1037              for (int i = 0; i < dests.Length; i++)
1038              {
1039                  if (dest + i >= RegisterConsts.RegisterZeroIndex)
1040                  {
1041                      break;
1042                  }
1043  
1044                  dests[outputIndex++] = Register(dest + i, RegisterType.Gpr);
1045              }
1046  
1047              if (outputIndex != dests.Length)
1048              {
1049                  Array.Resize(ref dests, outputIndex);
1050              }
1051  
1052              EmitTextureSample(context, type, flags, imm, componentMask, dests, sources);
1053          }
1054  
1055          private static void EmitTxq(
1056              EmitterContext context,
1057              TexQuery query,
1058              int imm,
1059              int componentMask,
1060              int srcA,
1061              int dest,
1062              bool isBindless)
1063          {
1064              if (dest == RegisterConsts.RegisterZeroIndex)
1065              {
1066                  return;
1067              }
1068  
1069              Operand Ra()
1070              {
1071                  if (srcA > RegisterConsts.RegisterZeroIndex)
1072                  {
1073                      return Const(0);
1074                  }
1075  
1076                  return context.Copy(Register(srcA++, RegisterType.Gpr));
1077              }
1078  
1079              List<Operand> sourcesList = new();
1080  
1081              if (isBindless)
1082              {
1083                  sourcesList.Add(Ra());
1084              }
1085  
1086              sourcesList.Add(Ra());
1087  
1088              Operand[] sources = sourcesList.ToArray();
1089  
1090              Operand GetDest()
1091              {
1092                  if (dest >= RegisterConsts.RegisterZeroIndex)
1093                  {
1094                      return null;
1095                  }
1096  
1097                  return Register(dest++, RegisterType.Gpr);
1098              }
1099  
1100              SamplerType type;
1101  
1102              if (isBindless)
1103              {
1104                  if (query == TexQuery.TexHeaderTextureType)
1105                  {
1106                      type = SamplerType.Texture2D | SamplerType.Multisample;
1107                  }
1108                  else
1109                  {
1110                      type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
1111                  }
1112              }
1113              else
1114              {
1115                  type = context.TranslatorContext.GpuAccessor.QuerySamplerType(imm);
1116              }
1117  
1118              TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
1119              SetBindingPair setAndBinding;
1120  
1121              switch (query)
1122              {
1123                  case TexQuery.TexHeaderDimension:
1124                      setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
1125                          Instruction.TextureQuerySize,
1126                          type,
1127                          TextureFormat.Unknown,
1128                          flags,
1129                          TextureOperation.DefaultCbufSlot,
1130                          imm);
1131  
1132                      for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
1133                      {
1134                          if ((compMask & 1) != 0)
1135                          {
1136                              Operand d = GetDest();
1137  
1138                              if (d == null)
1139                              {
1140                                  break;
1141                              }
1142  
1143                              context.Copy(d, context.TextureQuerySize(type, flags, setAndBinding, compIndex, sources));
1144                          }
1145                      }
1146                      break;
1147  
1148                  case TexQuery.TexHeaderTextureType:
1149                      setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
1150                          Instruction.TextureQuerySamples,
1151                          type,
1152                          TextureFormat.Unknown,
1153                          flags,
1154                          TextureOperation.DefaultCbufSlot,
1155                          imm);
1156  
1157                      if ((componentMask & 4) != 0)
1158                      {
1159                          // Skip first 2 components if necessary.
1160                          if ((componentMask & 1) != 0)
1161                          {
1162                              GetDest();
1163                          }
1164  
1165                          if ((componentMask & 2) != 0)
1166                          {
1167                              GetDest();
1168                          }
1169  
1170                          Operand d = GetDest();
1171  
1172                          if (d != null)
1173                          {
1174                              context.Copy(d, context.TextureQuerySamples(type, flags, setAndBinding, sources));
1175                          }
1176                      }
1177                      break;
1178  
1179                  default:
1180                      context.TranslatorContext.GpuAccessor.Log($"Invalid or unsupported query type \"{query}\".");
1181                      break;
1182              }
1183          }
1184  
1185          private static void EmitTextureSample(
1186              EmitterContext context,
1187              SamplerType type,
1188              TextureFlags flags,
1189              int handle,
1190              int componentMask,
1191              Operand[] dests,
1192              Operand[] sources)
1193          {
1194              SetBindingPair setAndBinding = flags.HasFlag(TextureFlags.Bindless) ? default : context.ResourceManager.GetTextureOrImageBinding(
1195                  Instruction.TextureSample,
1196                  type,
1197                  TextureFormat.Unknown,
1198                  flags,
1199                  TextureOperation.DefaultCbufSlot,
1200                  handle);
1201  
1202              context.TextureSample(type, flags, setAndBinding, componentMask, dests, sources);
1203          }
1204  
1205          private static SamplerType ConvertSamplerType(TexDim dimensions)
1206          {
1207              return dimensions switch
1208              {
1209                  TexDim._1d => SamplerType.Texture1D,
1210                  TexDim.Array1d => SamplerType.Texture1D | SamplerType.Array,
1211                  TexDim._2d => SamplerType.Texture2D,
1212                  TexDim.Array2d => SamplerType.Texture2D | SamplerType.Array,
1213                  TexDim._3d => SamplerType.Texture3D,
1214                  TexDim.Array3d => SamplerType.Texture3D | SamplerType.Array,
1215                  TexDim.Cube => SamplerType.TextureCube,
1216                  TexDim.ArrayCube => SamplerType.TextureCube | SamplerType.Array,
1217                  _ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."),
1218              };
1219          }
1220  
1221          private static SamplerType ConvertSamplerType(TexsTarget type)
1222          {
1223              switch (type)
1224              {
1225                  case TexsTarget.Texture1DLodZero:
1226                      return SamplerType.Texture1D;
1227  
1228                  case TexsTarget.Texture2D:
1229                  case TexsTarget.Texture2DLodZero:
1230                  case TexsTarget.Texture2DLodLevel:
1231                      return SamplerType.Texture2D;
1232  
1233                  case TexsTarget.Texture2DDepthCompare:
1234                  case TexsTarget.Texture2DLodLevelDepthCompare:
1235                  case TexsTarget.Texture2DLodZeroDepthCompare:
1236                      return SamplerType.Texture2D | SamplerType.Shadow;
1237  
1238                  case TexsTarget.Texture2DArray:
1239                  case TexsTarget.Texture2DArrayLodZero:
1240                      return SamplerType.Texture2D | SamplerType.Array;
1241  
1242                  case TexsTarget.Texture2DArrayLodZeroDepthCompare:
1243                      return SamplerType.Texture2D | SamplerType.Array | SamplerType.Shadow;
1244  
1245                  case TexsTarget.Texture3D:
1246                  case TexsTarget.Texture3DLodZero:
1247                      return SamplerType.Texture3D;
1248  
1249                  case TexsTarget.TextureCube:
1250                  case TexsTarget.TextureCubeLodLevel:
1251                      return SamplerType.TextureCube;
1252              }
1253  
1254              return SamplerType.None;
1255          }
1256  
1257          private static SamplerType ConvertSamplerType(TldsTarget type)
1258          {
1259              switch (type)
1260              {
1261                  case TldsTarget.Texture1DLodZero:
1262                  case TldsTarget.Texture1DLodLevel:
1263                      return SamplerType.Texture1D;
1264  
1265                  case TldsTarget.Texture2DLodZero:
1266                  case TldsTarget.Texture2DLodZeroOffset:
1267                  case TldsTarget.Texture2DLodLevel:
1268                  case TldsTarget.Texture2DLodLevelOffset:
1269                      return SamplerType.Texture2D;
1270  
1271                  case TldsTarget.Texture2DLodZeroMultisample:
1272                      return SamplerType.Texture2D | SamplerType.Multisample;
1273  
1274                  case TldsTarget.Texture3DLodZero:
1275                      return SamplerType.Texture3D;
1276  
1277                  case TldsTarget.Texture2DArrayLodZero:
1278                      return SamplerType.Texture2D | SamplerType.Array;
1279              }
1280  
1281              return SamplerType.None;
1282          }
1283  
1284          private static TextureFlags ConvertTextureFlags(TexsTarget type)
1285          {
1286              switch (type)
1287              {
1288                  case TexsTarget.Texture1DLodZero:
1289                  case TexsTarget.Texture2DLodZero:
1290                  case TexsTarget.Texture2DLodLevel:
1291                  case TexsTarget.Texture2DLodLevelDepthCompare:
1292                  case TexsTarget.Texture2DLodZeroDepthCompare:
1293                  case TexsTarget.Texture2DArrayLodZero:
1294                  case TexsTarget.Texture2DArrayLodZeroDepthCompare:
1295                  case TexsTarget.Texture3DLodZero:
1296                  case TexsTarget.TextureCubeLodLevel:
1297                      return TextureFlags.LodLevel;
1298  
1299                  case TexsTarget.Texture2D:
1300                  case TexsTarget.Texture2DDepthCompare:
1301                  case TexsTarget.Texture2DArray:
1302                  case TexsTarget.Texture3D:
1303                  case TexsTarget.TextureCube:
1304                      return TextureFlags.None;
1305              }
1306  
1307              return TextureFlags.None;
1308          }
1309  
1310          private static TextureFlags ConvertTextureFlags(TldsTarget type)
1311          {
1312              switch (type)
1313              {
1314                  case TldsTarget.Texture1DLodZero:
1315                  case TldsTarget.Texture1DLodLevel:
1316                  case TldsTarget.Texture2DLodZero:
1317                  case TldsTarget.Texture2DLodLevel:
1318                  case TldsTarget.Texture2DLodZeroMultisample:
1319                  case TldsTarget.Texture3DLodZero:
1320                  case TldsTarget.Texture2DArrayLodZero:
1321                      return TextureFlags.LodLevel;
1322  
1323                  case TldsTarget.Texture2DLodZeroOffset:
1324                  case TldsTarget.Texture2DLodLevelOffset:
1325                      return TextureFlags.LodLevel | TextureFlags.Offset;
1326              }
1327  
1328              return TextureFlags.None;
1329          }
1330      }
1331  }