diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation')
4 files changed, 115 insertions, 11 deletions
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index ad955278..22321543 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation.Optimizations @@ -31,7 +32,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp)) + if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp) && + !GenerateBindlessAccess(block, resourceManager, gpuAccessor, texOp, node)) { // If we can't do bindless elimination, remove the texture operation. // Set any destination variables to zero. @@ -46,6 +48,88 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } + private static bool GenerateBindlessAccess( + BasicBlock block, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TextureOperation texOp, + LinkedListNode<INode> node) + { + if (!gpuAccessor.QueryHostSupportsSeparateSampler()) + { + // We depend on combining samplers and textures in the shader being supported for this. + + return false; + } + + Operand nvHandle = texOp.GetSource(0); + + if (nvHandle.AsgOp is not Operation handleOp || + handleOp.Inst != Instruction.Load || + handleOp.StorageKind != StorageKind.Input) + { + // Right now, we only allow bindless access when the handle comes from a shader input. + // 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; + } + + Operand textureHandle = OperandHelper.Local(); + 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))); + + int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool()); + + block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, textureIndex, textureHandle, OperandHelper.Const(texturePoolLength - 1))); + + texOp.SetSource(0, textureIndex); + + bool hasSampler = !texOp.Inst.IsImage(); + + int textureBinding = resourceManager.GetTextureOrImageBinding( + texOp.Inst, + texOp.Type, + texOp.Format, + texOp.Flags & ~TextureFlags.Bindless, + 0, + TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct), + texturePoolLength, + hasSampler); + + if (hasSampler) + { + Operand samplerIndex = OperandHelper.Local(); + + int samplerPoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QuerySamplerArrayLengthFromPool()); + + block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, samplerIndex, samplerHandle, OperandHelper.Const(samplerPoolLength - 1))); + + texOp.InsertSource(1, samplerIndex); + + int samplerBinding = resourceManager.GetTextureOrImageBinding( + texOp.Inst, + SamplerType.None, + texOp.Format, + TextureFlags.None, + 0, + TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct), + samplerPoolLength); + + texOp.TurnIntoArray(textureBinding, samplerBinding); + } + else + { + texOp.TurnIntoArray(textureBinding); + } + + return true; + } + private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp) { if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery()) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs index 7543d1c2..f2be7975 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private const int HardcodedArrayLengthOgl = 4; // 1 and 0 elements are not considered arrays anymore. - private const int MinimumArrayLength = 2; + public const int MinimumArrayLength = 2; public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index e9fe0b1e..890501c9 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet<int> _usedConstantBufferBindings; - private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, SamplerType Type, TextureFormat Format); + private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, bool Separate, SamplerType Type, TextureFormat Format); private struct TextureMeta { @@ -225,7 +225,8 @@ namespace Ryujinx.Graphics.Shader.Translation TextureFlags flags, int cbufSlot, int handle, - int arrayLength = 1) + int arrayLength = 1, + bool separate = false) { inst &= Instruction.Mask; bool isImage = inst.IsImage(); @@ -239,7 +240,18 @@ namespace Ryujinx.Graphics.Shader.Translation format = TextureFormat.Unknown; } - int binding = GetTextureOrImageBinding(cbufSlot, handle, arrayLength, type, format, isImage, intCoords, isWrite, accurateType, coherent); + int binding = GetTextureOrImageBinding( + cbufSlot, + handle, + arrayLength, + type, + format, + isImage, + intCoords, + isWrite, + accurateType, + coherent, + separate); _gpuAccessor.RegisterTexture(handle, cbufSlot); @@ -256,9 +268,10 @@ namespace Ryujinx.Graphics.Shader.Translation bool intCoords, bool write, bool accurateType, - bool coherent) + bool coherent, + bool separate) { - var dimensions = type.GetDimensions(); + var dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); var dict = isImage ? _usedImages : _usedTextures; var usageFlags = TextureUsageFlags.None; @@ -290,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.Translation // For array textures, we also want to use type as key, // since we may have texture handles stores in the same buffer, but for textures with different types. var keyType = arrayLength > 1 ? type : SamplerType.None; - var info = new TextureInfo(cbufSlot, handle, arrayLength, keyType, format); + var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format); var meta = new TextureMeta() { AccurateType = accurateType, @@ -332,6 +345,10 @@ namespace Ryujinx.Graphics.Shader.Translation ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}" : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; } + else if (type == SamplerType.None) + { + nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}"; + } else { nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; @@ -341,6 +358,7 @@ namespace Ryujinx.Graphics.Shader.Translation isImage ? 3 : 2, binding, arrayLength, + separate, $"{_stagePrefix}_{nameSuffix}", meta.Type, info.Format, @@ -495,6 +513,7 @@ namespace Ryujinx.Graphics.Shader.Translation info.CbufSlot, info.Handle, info.ArrayLength, + info.Separate, meta.UsageFlags)); } @@ -514,6 +533,7 @@ namespace Ryujinx.Graphics.Shader.Translation info.CbufSlot, info.Handle, info.ArrayLength, + info.Separate, meta.UsageFlags)); } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 581f4372..10653558 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (Stage == ShaderStage.Vertex) { int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding; - TextureDefinition indexBuffer = new(2, ibBinding, 1, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); + TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(indexBuffer); int inputMap = _program.AttributeUsage.UsedInputAttributes; @@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int location = BitOperations.TrailingZeroCount(inputMap); int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location); - TextureDefinition vaBuffer = new(2, binding, 1, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); + TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(vaBuffer); inputMap &= ~(1 << location); @@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Translation else if (Stage == ShaderStage.Geometry) { int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding; - TextureDefinition remapBuffer = new(2, trbBinding, 1, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); + TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(remapBuffer); int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; |
