From c41fddd25e8ddd3cf0b3cefeaf6595d2e4ede0fa Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 26 May 2024 15:20:10 -0300 Subject: [PATCH] Vulkan: Extend full bindless to cover cases with phi nodes (#6853) * Key textures using set and binding (rather than just binding) * Extend full bindless to cover cases with phi nodes * Log error on bindless access failure * Shader cache version bump * Remove constant buffer match to reduce the chances of full bindless triggering * Re-enable it for constant buffers, paper mario does actually need it * Format whitespace --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Optimizations/BindlessElimination.cs | 55 +++++++++++++++---- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 990c6ba3b..fbf48f017 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 = 6870; + private const uint CodeGenVersion = 6852; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 29501b710..02a83fbe4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -38,6 +38,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // If we can't do bindless elimination, remove the texture operation. // Set any destination variables to zero. + string typeName = texOp.Inst.IsImage() + ? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()) + : texOp.Type.ToGlslTextureType(); + + gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\"."); + for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++) { block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0))); @@ -62,17 +68,22 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return false; } - Operand nvHandle = texOp.GetSource(0); + Operand bindlessHandle = texOp.GetSource(0); - if (nvHandle.AsgOp is not Operation handleOp || - handleOp.Inst != Instruction.Load || - (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer)) + if (bindlessHandle.AsgOp is PhiNode phi) { - // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer. - // This is an artificial limitation to prevent it from being used in cases where it - // would have a large performance impact of loading all textures in the pool. - // It might be removed in the future, if we can mitigate the performance impact. + for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++) + { + Operand phiSource = phi.GetSource(srcIndex); + if (phiSource.AsgOp is not PhiNode && !IsBindlessAccessAllowed(phiSource)) + { + return false; + } + } + } + else if (!IsBindlessAccessAllowed(bindlessHandle)) + { return false; } @@ -80,8 +91,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand samplerHandle = OperandHelper.Local(); Operand textureIndex = OperandHelper.Local(); - block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff))); - block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20))); + block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, bindlessHandle, OperandHelper.Const(0xfffff))); + block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, bindlessHandle, OperandHelper.Const(20))); int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool()); @@ -130,6 +141,30 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return true; } + private static bool IsBindlessAccessAllowed(Operand nvHandle) + { + if (nvHandle.Type == OperandType.ConstantBuffer) + { + // Bindless access with handles from constant buffer is allowed. + + return true; + } + + if (nvHandle.AsgOp is not Operation handleOp || + handleOp.Inst != Instruction.Load || + (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer)) + { + // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer. + // This is an artificial limitation to prevent it from being used in cases where it + // would have a large performance impact of loading all textures in the pool. + // It might be removed in the future, if we can mitigate the performance impact. + + return false; + } + + return true; + } + private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp) { if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())