/ src / Ryujinx.Graphics.Shader / Instructions / InstEmitFloatComparison.cs
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  }