Просмотр исходного кода

Extend bindless elimination to catch a few more specific cases (#6921)

* Catch more cases on bindless elimination

* Match blocks with the same comparison condition

* Shader cache version bump
gdkchan 1 год назад
Родитель
Сommit
3193ef1083

+ 1 - 1
src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         private const ushort FileFormatVersionMajor = 1;
         private const ushort FileFormatVersionMinor = 2;
         private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
-        private const uint CodeGenVersion = 6852;
+        private const uint CodeGenVersion = 6921;
 
         private const string SharedTocFileName = "shared.toc";
         private const string SharedDataFileName = "shared.data";

+ 1 - 1
src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs

@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
 
             if (op.BVal)
             {
-                context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
+                context.Copy(dest, context.ConditionalSelect(res, ConstF(1), ConstF(0)));
             }
             else
             {

+ 20 - 0
src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs

@@ -156,6 +156,26 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
             return false;
         }
 
+        public static bool IsComparison(this Instruction inst)
+        {
+            switch (inst & Instruction.Mask)
+            {
+                case Instruction.CompareEqual:
+                case Instruction.CompareGreater:
+                case Instruction.CompareGreaterOrEqual:
+                case Instruction.CompareGreaterOrEqualU32:
+                case Instruction.CompareGreaterU32:
+                case Instruction.CompareLess:
+                case Instruction.CompareLessOrEqual:
+                case Instruction.CompareLessOrEqualU32:
+                case Instruction.CompareLessU32:
+                case Instruction.CompareNotEqual:
+                    return true;
+            }
+
+            return false;
+        }
+
         public static bool IsTextureQuery(this Instruction inst)
         {
             inst &= Instruction.Mask;

+ 4 - 4
src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs

@@ -141,16 +141,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return true;
         }
 
-        private static bool IsBindlessAccessAllowed(Operand nvHandle)
+        private static bool IsBindlessAccessAllowed(Operand bindlessHandle)
         {
-            if (nvHandle.Type == OperandType.ConstantBuffer)
+            if (bindlessHandle.Type == OperandType.ConstantBuffer)
             {
                 // Bindless access with handles from constant buffer is allowed.
 
                 return true;
             }
 
-            if (nvHandle.AsgOp is not Operation handleOp ||
+            if (bindlessHandle.AsgOp is not Operation handleOp ||
                 handleOp.Inst != Instruction.Load ||
                 (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
             {
@@ -300,7 +300,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                         resourceManager,
                         gpuAccessor,
                         texOp,
-                        TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType),
+                        TextureHandle.PackOffsets(src0.GetCbufOffset(), (src1.Value >> 20) & 0xfff, handleType),
                         TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
                         rewriteSamplerType,
                         isImage: false);

+ 5 - 3
src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs

@@ -126,7 +126,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                     continue;
                 }
 
-                if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp)
+                Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
+
+                if (bindlessHandle.AsgOp is not Operation handleAsgOp)
                 {
                     continue;
                 }
@@ -137,8 +139,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
 
                 if (handleAsgOp.Inst == Instruction.BitwiseOr)
                 {
-                    Operand src0 = handleAsgOp.GetSource(0);
-                    Operand src1 = handleAsgOp.GetSource(1);
+                    Operand src0 = Utils.FindLastOperation(handleAsgOp.GetSource(0), block);
+                    Operand src1 = Utils.FindLastOperation(handleAsgOp.GetSource(1), block);
 
                     if (src0.Type == OperandType.ConstantBuffer && src1.AsgOp is Operation)
                     {

+ 3 - 18
src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs

@@ -152,18 +152,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
         {
             // If all phi sources are the same, we can propagate it and remove the phi.
 
-            Operand firstSrc = phi.GetSource(0);
-
-            for (int index = 1; index < phi.SourcesCount; index++)
+            if (!Utils.AreAllSourcesTheSameOperand(phi))
             {
-                if (!IsSameOperand(firstSrc, phi.GetSource(index)))
-                {
-                    return false;
-                }
+                return false;
             }
 
             // All sources are equal, we can propagate the value.
 
+            Operand firstSrc = phi.GetSource(0);
             Operand dest = phi.Dest;
 
             INode[] uses = dest.UseOps.ToArray();
@@ -182,17 +178,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return true;
         }
 
-        private static bool IsSameOperand(Operand x, Operand y)
-        {
-            if (x.Type != y.Type || x.Value != y.Value)
-            {
-                return false;
-            }
-
-            // TODO: Handle Load operations with the same storage and the same constant parameters.
-            return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
-        }
-
         private static bool PropagatePack(Operation packOp)
         {
             // Propagate pack source operands to uses by unpack

+ 30 - 0
src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs

@@ -31,6 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                     TryEliminateBitwiseOr(operation);
                     break;
 
+                case Instruction.CompareNotEqual:
+                    TryEliminateCompareNotEqual(operation);
+                    break;
+
                 case Instruction.ConditionalSelect:
                     TryEliminateConditionalSelect(operation);
                     break;
@@ -174,6 +178,32 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             }
         }
 
+        private static void TryEliminateCompareNotEqual(Operation operation)
+        {
+            // Comparison instruction returns 0 if the result is false, and -1 if true.
+            // Doing a not equal zero comparison on the result is redundant, so we can just copy the first result in this case.
+
+            Operand lhs = operation.GetSource(0);
+            Operand rhs = operation.GetSource(1);
+
+            if (lhs.Type == OperandType.Constant)
+            {
+                (lhs, rhs) = (rhs, lhs);
+            }
+
+            if (rhs.Type != OperandType.Constant || rhs.Value != 0)
+            {
+                return;
+            }
+
+            if (lhs.AsgOp is not Operation compareOp || !compareOp.Inst.IsComparison())
+            {
+                return;
+            }
+
+            operation.TurnIntoCopy(lhs);
+        }
+
         private static void TryEliminateConditionalSelect(Operation operation)
         {
             Operand cond = operation.GetSource(0);

+ 73 - 3
src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs

@@ -34,6 +34,50 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
         }
 
+        private static bool IsSameOperand(Operand x, Operand y)
+        {
+            if (x.Type != y.Type || x.Value != y.Value)
+            {
+                return false;
+            }
+
+            // TODO: Handle Load operations with the same storage and the same constant parameters.
+            return x == y || x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
+        }
+
+        private static bool AreAllSourcesEqual(INode node, INode otherNode)
+        {
+            if (node.SourcesCount != otherNode.SourcesCount)
+            {
+                return false;
+            }
+
+            for (int index = 0; index < node.SourcesCount; index++)
+            {
+                if (!IsSameOperand(node.GetSource(index), otherNode.GetSource(index)))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public static bool AreAllSourcesTheSameOperand(INode node)
+        {
+            Operand firstSrc = node.GetSource(0);
+
+            for (int index = 1; index < node.SourcesCount; index++)
+            {
+                if (!IsSameOperand(firstSrc, node.GetSource(index)))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
         private static Operation FindBranchSource(BasicBlock block)
         {
             foreach (BasicBlock sourceBlock in block.Predecessors)
@@ -55,6 +99,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return inst == Instruction.BranchIfFalse || inst == Instruction.BranchIfTrue;
         }
 
+        private static bool IsSameCondition(Operand currentCondition, Operand queryCondition)
+        {
+            if (currentCondition == queryCondition)
+            {
+                return true;
+            }
+
+            return currentCondition.AsgOp is Operation currentOperation &&
+                queryCondition.AsgOp is Operation queryOperation &&
+                currentOperation.Inst == queryOperation.Inst &&
+                AreAllSourcesEqual(currentOperation, queryOperation);
+        }
+
         private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock)
         {
             // Check if all the conditions for the query block are satisfied by the current block.
@@ -70,10 +127,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
 
             return currentBranch != null && queryBranch != null &&
                    currentBranch.Inst == queryBranch.Inst &&
-                   currentCondition == queryCondition;
+                   IsSameCondition(currentCondition, queryCondition);
         }
 
-        public static Operand FindLastOperation(Operand source, BasicBlock block)
+        public static Operand FindLastOperation(Operand source, BasicBlock block, bool recurse = true)
         {
             if (source.AsgOp is PhiNode phiNode)
             {
@@ -84,10 +141,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                 for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
                 {
                     BasicBlock phiBlock = phiNode.GetBlock(i);
+                    Operand phiSource = phiNode.GetSource(i);
 
                     if (BlockConditionsMatch(block, phiBlock))
                     {
-                        return phiNode.GetSource(i);
+                        return phiSource;
+                    }
+                    else if (recurse && phiSource.AsgOp is PhiNode)
+                    {
+                        // Phi source is another phi.
+                        // Let's check if that phi has a block that matches our condition.
+
+                        Operand match = FindLastOperation(phiSource, block, false);
+
+                        if (match != phiSource)
+                        {
+                            return match;
+                        }
                     }
                 }
             }