From 3193ef10833bc0d27e2701c7759ab02674d672d3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 16 Jun 2024 14:46:27 -0300 Subject: [PATCH 1/2] 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 --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Instructions/InstEmitPredicate.cs | 2 +- .../IntermediateRepresentation/Instruction.cs | 20 +++++ .../Optimizations/BindlessElimination.cs | 8 +- .../Optimizations/BindlessToArray.cs | 8 +- .../Translation/Optimizations/Optimizer.cs | 21 +---- .../Optimizations/Simplification.cs | 30 ++++++++ .../Translation/Optimizations/Utils.cs | 76 ++++++++++++++++++- 8 files changed, 137 insertions(+), 30 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index fbf48f017e..c4b5a13801 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/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"; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs index 630162ade5..1d8651254f 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs +++ b/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 { diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 8703e660e0..273a38a5b6 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/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; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 02a83fbe40..1f2f79a2dd 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/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); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs index 8eed139d6c..1e0b3b645a 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs +++ b/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) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 49eb3a89b3..1be7c5c520 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/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 diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs index a509fcb425..097c8aa88c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs +++ b/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); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index 74a6d5a1e7..23180ff825 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/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; + } } } } From 311ca3c3f1719c0effeedfb8cf90d9f2675ef8a5 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:47:47 -0400 Subject: [PATCH 2/2] fix: for pooled memory used for reference types, clear it on return to the pool so that it doesn't prevent GC of the instances it contained (#6937) --- src/Ryujinx.Common/Memory/MemoryOwner.cs | 2 +- src/Ryujinx.Common/Memory/SpanOwner.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Common/Memory/MemoryOwner.cs b/src/Ryujinx.Common/Memory/MemoryOwner.cs index 5e567ab8d6..b7fe1db778 100644 --- a/src/Ryujinx.Common/Memory/MemoryOwner.cs +++ b/src/Ryujinx.Common/Memory/MemoryOwner.cs @@ -124,7 +124,7 @@ namespace Ryujinx.Common.Memory if (array is not null) { - ArrayPool.Shared.Return(array); + ArrayPool.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences()); } } diff --git a/src/Ryujinx.Common/Memory/SpanOwner.cs b/src/Ryujinx.Common/Memory/SpanOwner.cs index a4b4adf32b..acb20bcad6 100644 --- a/src/Ryujinx.Common/Memory/SpanOwner.cs +++ b/src/Ryujinx.Common/Memory/SpanOwner.cs @@ -108,7 +108,7 @@ namespace Ryujinx.Common.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - ArrayPool.Shared.Return(_array); + ArrayPool.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences()); } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 6595ecef2a..91c6bded2c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -616,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } - ArrayPool.Shared.Return(syncObjsArray); + ArrayPool.Shared.Return(syncObjsArray, true); return result; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index b1af06b0d5..21c2730bf9 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - ArrayPool>.Shared.Return(syncNodesArray); + ArrayPool>.Shared.Return(syncNodesArray, true); } _context.CriticalSection.Leave();