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 }