InstEmitFloatComparison.cs
1 using Ryujinx.Graphics.Shader.Decoders; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using Ryujinx.Graphics.Shader.Translation; 4 using System; 5 using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; 6 using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; 7 using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; 8 9 namespace Ryujinx.Graphics.Shader.Instructions 10 { 11 static partial class InstEmit 12 { 13 public static void DsetR(EmitterContext context) 14 { 15 InstDsetR op = context.GetOp<InstDsetR>(); 16 17 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 18 var srcB = GetSrcReg(context, op.SrcB, isFP64: true); 19 20 EmitFset( 21 context, 22 op.FComp, 23 op.Bop, 24 srcA, 25 srcB, 26 op.SrcPred, 27 op.SrcPredInv, 28 op.Dest, 29 op.AbsA, 30 op.AbsB, 31 op.NegA, 32 op.NegB, 33 op.BVal, 34 op.WriteCC, 35 isFP64: true); 36 } 37 38 public static void DsetI(EmitterContext context) 39 { 40 InstDsetI op = context.GetOp<InstDsetI>(); 41 42 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 43 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true); 44 45 EmitFset( 46 context, 47 op.FComp, 48 op.Bop, 49 srcA, 50 srcB, 51 op.SrcPred, 52 op.SrcPredInv, 53 op.Dest, 54 op.AbsA, 55 op.AbsB, 56 op.NegA, 57 op.NegB, 58 op.BVal, 59 op.WriteCC, 60 isFP64: true); 61 } 62 63 public static void DsetC(EmitterContext context) 64 { 65 InstDsetC op = context.GetOp<InstDsetC>(); 66 67 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 68 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 69 70 EmitFset( 71 context, 72 op.FComp, 73 op.Bop, 74 srcA, 75 srcB, 76 op.SrcPred, 77 op.SrcPredInv, 78 op.Dest, 79 op.AbsA, 80 op.AbsB, 81 op.NegA, 82 op.NegB, 83 op.BVal, 84 op.WriteCC, 85 isFP64: true); 86 } 87 88 public static void DsetpR(EmitterContext context) 89 { 90 InstDsetpR op = context.GetOp<InstDsetpR>(); 91 92 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 93 var srcB = GetSrcReg(context, op.SrcB, isFP64: true); 94 95 EmitFsetp( 96 context, 97 op.FComp, 98 op.Bop, 99 srcA, 100 srcB, 101 op.SrcPred, 102 op.SrcPredInv, 103 op.DestPred, 104 op.DestPredInv, 105 op.AbsA, 106 op.AbsB, 107 op.NegA, 108 op.NegB, 109 writeCC: false, 110 isFP64: true); 111 } 112 113 public static void DsetpI(EmitterContext context) 114 { 115 InstDsetpI op = context.GetOp<InstDsetpI>(); 116 117 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 118 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true); 119 120 EmitFsetp( 121 context, 122 op.FComp, 123 op.Bop, 124 srcA, 125 srcB, 126 op.SrcPred, 127 op.SrcPredInv, 128 op.DestPred, 129 op.DestPredInv, 130 op.AbsA, 131 op.AbsB, 132 op.NegA, 133 op.NegB, 134 writeCC: false, 135 isFP64: true); 136 } 137 138 public static void DsetpC(EmitterContext context) 139 { 140 InstDsetpC op = context.GetOp<InstDsetpC>(); 141 142 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 143 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 144 145 EmitFsetp( 146 context, 147 op.FComp, 148 op.Bop, 149 srcA, 150 srcB, 151 op.SrcPred, 152 op.SrcPredInv, 153 op.DestPred, 154 op.DestPredInv, 155 op.AbsA, 156 op.AbsB, 157 op.NegA, 158 op.NegB, 159 writeCC: false, 160 isFP64: true); 161 } 162 163 public static void FcmpR(EmitterContext context) 164 { 165 InstFcmpR op = context.GetOp<InstFcmpR>(); 166 167 var srcA = GetSrcReg(context, op.SrcA); 168 var srcB = GetSrcReg(context, op.SrcB); 169 var srcC = GetSrcReg(context, op.SrcC); 170 171 EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest); 172 } 173 174 public static void FcmpI(EmitterContext context) 175 { 176 InstFcmpI op = context.GetOp<InstFcmpI>(); 177 178 var srcA = GetSrcReg(context, op.SrcA); 179 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 180 var srcC = GetSrcReg(context, op.SrcC); 181 182 EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest); 183 } 184 185 public static void FcmpC(EmitterContext context) 186 { 187 InstFcmpC op = context.GetOp<InstFcmpC>(); 188 189 var srcA = GetSrcReg(context, op.SrcA); 190 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 191 var srcC = GetSrcReg(context, op.SrcC); 192 193 EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest); 194 } 195 196 public static void FcmpRc(EmitterContext context) 197 { 198 InstFcmpRc op = context.GetOp<InstFcmpRc>(); 199 200 var srcA = GetSrcReg(context, op.SrcA); 201 var srcB = GetSrcReg(context, op.SrcC); 202 var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 203 204 EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest); 205 } 206 207 public static void FsetR(EmitterContext context) 208 { 209 InstFsetR op = context.GetOp<InstFsetR>(); 210 211 var srcA = GetSrcReg(context, op.SrcA); 212 var srcB = GetSrcReg(context, op.SrcB); 213 214 EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC); 215 } 216 217 public static void FsetC(EmitterContext context) 218 { 219 InstFsetC op = context.GetOp<InstFsetC>(); 220 221 var srcA = GetSrcReg(context, op.SrcA); 222 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 223 224 EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC); 225 } 226 227 public static void FsetI(EmitterContext context) 228 { 229 InstFsetI op = context.GetOp<InstFsetI>(); 230 231 var srcA = GetSrcReg(context, op.SrcA); 232 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 233 234 EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC); 235 } 236 237 public static void FsetpR(EmitterContext context) 238 { 239 InstFsetpR op = context.GetOp<InstFsetpR>(); 240 241 var srcA = GetSrcReg(context, op.SrcA); 242 var srcB = GetSrcReg(context, op.SrcB); 243 244 EmitFsetp( 245 context, 246 op.FComp, 247 op.Bop, 248 srcA, 249 srcB, 250 op.SrcPred, 251 op.SrcPredInv, 252 op.DestPred, 253 op.DestPredInv, 254 op.AbsA, 255 op.AbsB, 256 op.NegA, 257 op.NegB, 258 op.WriteCC); 259 } 260 261 public static void FsetpI(EmitterContext context) 262 { 263 InstFsetpI op = context.GetOp<InstFsetpI>(); 264 265 var srcA = GetSrcReg(context, op.SrcA); 266 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 267 268 EmitFsetp( 269 context, 270 op.FComp, 271 op.Bop, 272 srcA, 273 srcB, 274 op.SrcPred, 275 op.SrcPredInv, 276 op.DestPred, 277 op.DestPredInv, 278 op.AbsA, 279 op.AbsB, 280 op.NegA, 281 op.NegB, 282 op.WriteCC); 283 } 284 285 public static void FsetpC(EmitterContext context) 286 { 287 InstFsetpC op = context.GetOp<InstFsetpC>(); 288 289 var srcA = GetSrcReg(context, op.SrcA); 290 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 291 292 EmitFsetp( 293 context, 294 op.FComp, 295 op.Bop, 296 srcA, 297 srcB, 298 op.SrcPred, 299 op.SrcPredInv, 300 op.DestPred, 301 op.DestPredInv, 302 op.AbsA, 303 op.AbsB, 304 op.NegA, 305 op.NegB, 306 op.WriteCC); 307 } 308 309 public static void Hset2R(EmitterContext context) 310 { 311 InstHset2R op = context.GetOp<InstHset2R>(); 312 313 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 314 var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB); 315 316 EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval); 317 } 318 319 public static void Hset2I(EmitterContext context) 320 { 321 InstHset2I op = context.GetOp<InstHset2I>(); 322 323 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 324 var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1); 325 326 EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval); 327 } 328 329 public static void Hset2C(EmitterContext context) 330 { 331 InstHset2C op = context.GetOp<InstHset2C>(); 332 333 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 334 var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, false); 335 336 EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval); 337 } 338 339 public static void Hsetp2R(EmitterContext context) 340 { 341 InstHsetp2R op = context.GetOp<InstHsetp2R>(); 342 343 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 344 var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB); 345 346 EmitHsetp2(context, op.FComp2, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd); 347 } 348 349 public static void Hsetp2I(EmitterContext context) 350 { 351 InstHsetp2I op = context.GetOp<InstHsetp2I>(); 352 353 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 354 var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1); 355 356 EmitHsetp2(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd); 357 } 358 359 public static void Hsetp2C(EmitterContext context) 360 { 361 InstHsetp2C op = context.GetOp<InstHsetp2C>(); 362 363 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 364 var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, op.AbsB); 365 366 EmitHsetp2(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd); 367 } 368 369 private static void EmitFcmp(EmitterContext context, FComp cmpOp, Operand srcA, Operand srcB, Operand srcC, int rd) 370 { 371 Operand cmpRes = GetFPComparison(context, cmpOp, srcC, ConstF(0)); 372 373 Operand res = context.ConditionalSelect(cmpRes, srcA, srcB); 374 375 context.Copy(GetDest(rd), res); 376 } 377 378 private static void EmitFset( 379 EmitterContext context, 380 FComp cmpOp, 381 BoolOp logicOp, 382 Operand srcA, 383 Operand srcB, 384 int srcPred, 385 bool srcPredInv, 386 int rd, 387 bool absoluteA, 388 bool absoluteB, 389 bool negateA, 390 bool negateB, 391 bool boolFloat, 392 bool writeCC, 393 bool isFP64 = false) 394 { 395 Instruction fpType = isFP64 ? Instruction.FP64 : Instruction.FP32; 396 397 srcA = context.FPAbsNeg(srcA, absoluteA, negateA, fpType); 398 srcB = context.FPAbsNeg(srcB, absoluteB, negateB, fpType); 399 400 Operand res = GetFPComparison(context, cmpOp, srcA, srcB, fpType); 401 Operand pred = GetPredicate(context, srcPred, srcPredInv); 402 403 res = GetPredLogicalOp(context, logicOp, res, pred); 404 405 Operand dest = GetDest(rd); 406 407 if (boolFloat) 408 { 409 res = context.ConditionalSelect(res, ConstF(1), Const(0)); 410 411 context.Copy(dest, res); 412 413 SetFPZnFlags(context, res, writeCC); 414 } 415 else 416 { 417 context.Copy(dest, res); 418 419 SetZnFlags(context, res, writeCC, extended: false); 420 } 421 } 422 423 private static void EmitFsetp( 424 EmitterContext context, 425 FComp cmpOp, 426 BoolOp logicOp, 427 Operand srcA, 428 Operand srcB, 429 int srcPred, 430 bool srcPredInv, 431 int destPred, 432 int destPredInv, 433 bool absoluteA, 434 bool absoluteB, 435 bool negateA, 436 bool negateB, 437 bool writeCC, 438 bool isFP64 = false) 439 { 440 Instruction fpType = isFP64 ? Instruction.FP64 : Instruction.FP32; 441 442 srcA = context.FPAbsNeg(srcA, absoluteA, negateA, fpType); 443 srcB = context.FPAbsNeg(srcB, absoluteB, negateB, fpType); 444 445 Operand p0Res = GetFPComparison(context, cmpOp, srcA, srcB, fpType); 446 Operand p1Res = context.BitwiseNot(p0Res); 447 Operand pred = GetPredicate(context, srcPred, srcPredInv); 448 449 p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred); 450 p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred); 451 452 context.Copy(Register(destPred, RegisterType.Predicate), p0Res); 453 context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res); 454 } 455 456 private static void EmitHset2( 457 EmitterContext context, 458 FComp cmpOp, 459 BoolOp logicOp, 460 Operand[] srcA, 461 Operand[] srcB, 462 int srcPred, 463 bool srcPredInv, 464 int rd, 465 bool boolFloat) 466 { 467 Operand[] res = new Operand[2]; 468 469 res[0] = GetFPComparison(context, cmpOp, srcA[0], srcB[0]); 470 res[1] = GetFPComparison(context, cmpOp, srcA[1], srcB[1]); 471 472 Operand pred = GetPredicate(context, srcPred, srcPredInv); 473 474 res[0] = GetPredLogicalOp(context, logicOp, res[0], pred); 475 res[1] = GetPredLogicalOp(context, logicOp, res[1], pred); 476 477 if (boolFloat) 478 { 479 res[0] = context.ConditionalSelect(res[0], ConstF(1), Const(0)); 480 res[1] = context.ConditionalSelect(res[1], ConstF(1), Const(0)); 481 482 context.Copy(GetDest(rd), context.PackHalf2x16(res[0], res[1])); 483 } 484 else 485 { 486 Operand low = context.BitwiseAnd(res[0], Const(0xffff)); 487 Operand high = context.ShiftLeft(res[1], Const(16)); 488 489 Operand packed = context.BitwiseOr(low, high); 490 491 context.Copy(GetDest(rd), packed); 492 } 493 } 494 495 private static void EmitHsetp2( 496 EmitterContext context, 497 FComp cmpOp, 498 BoolOp logicOp, 499 Operand[] srcA, 500 Operand[] srcB, 501 int srcPred, 502 bool srcPredInv, 503 int destPred, 504 int destPredInv, 505 bool hAnd) 506 { 507 Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]); 508 Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]); 509 510 if (hAnd) 511 { 512 p0Res = context.BitwiseAnd(p0Res, p1Res); 513 p1Res = context.BitwiseNot(p0Res); 514 } 515 516 Operand pred = GetPredicate(context, srcPred, srcPredInv); 517 518 p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred); 519 p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred); 520 521 context.Copy(Register(destPred, RegisterType.Predicate), p0Res); 522 context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res); 523 } 524 525 private static Operand GetFPComparison(EmitterContext context, FComp cond, Operand srcA, Operand srcB, Instruction fpType = Instruction.FP32) 526 { 527 Operand res; 528 529 if (cond == FComp.T) 530 { 531 res = Const(IrConsts.True); 532 } 533 else if (cond == FComp.F) 534 { 535 res = Const(IrConsts.False); 536 } 537 else if (cond == FComp.Nan || cond == FComp.Num) 538 { 539 res = context.BitwiseOr(context.IsNan(srcA, fpType), context.IsNan(srcB, fpType)); 540 541 if (cond == FComp.Num) 542 { 543 res = context.BitwiseNot(res); 544 } 545 } 546 else 547 { 548 var inst = (cond & ~FComp.Nan) switch 549 { 550 FComp.Lt => Instruction.CompareLess, 551 FComp.Eq => Instruction.CompareEqual, 552 FComp.Le => Instruction.CompareLessOrEqual, 553 FComp.Gt => Instruction.CompareGreater, 554 FComp.Ne => Instruction.CompareNotEqual, 555 FComp.Ge => Instruction.CompareGreaterOrEqual, 556 _ => throw new ArgumentException($"Unexpected condition \"{cond}\"."), 557 }; 558 res = context.Add(inst | fpType, Local(), srcA, srcB); 559 560 if ((cond & FComp.Nan) != 0) 561 { 562 res = context.BitwiseOr(res, context.IsNan(srcA, fpType)); 563 res = context.BitwiseOr(res, context.IsNan(srcB, fpType)); 564 } 565 } 566 567 return res; 568 } 569 } 570 }