diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2020-05-27 11:07:10 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-27 16:07:10 +0200 |
| commit | 5795bb15282498b3824a5d15fe1ff78b85a18c23 (patch) | |
| tree | 6d4ee54c218e81fc6efaad279a5b1ade3ca8ec59 /Ryujinx.Graphics.Shader | |
| parent | 0b6d206daad7202d4e271118b631feb7dd363bbc (diff) | |
Support separate textures and samplers (#1216)
* Support separate textures and samplers
* Add missing bindless flag, fix SNORM format on buffer textures
* Add missing separation
* Add comments about the new handles
Diffstat (limited to 'Ryujinx.Graphics.Shader')
9 files changed, 183 insertions, 19 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index a45c10c3..bd947ab7 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -324,6 +324,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (!images.TryAdd(imageName, texOp)) { + // Ensure that all texture operations share the same format. + // This avoid errors like mismatched formats. + texOp.Format = images[imageName].Format; + continue; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index e9a37d87..4c3f802a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - suffix = texOp.Handle.ToString(); + suffix = texOp.Handle.ToString("X"); if ((texOp.Type & SamplerType.Indexed) != 0) { @@ -242,7 +242,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) { - string suffix = texOp.Handle.ToString(); + string suffix = texOp.Handle.ToString("X"); if ((texOp.Type & SamplerType.Indexed) != 0) { diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 7bed3f30..43e5822e 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -879,6 +879,8 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.IsBindless) { sourcesList.Add(Ra()); + + flags |= TextureFlags.Bindless; } SamplerType type = ConvertSamplerType(op.Dimensions); @@ -1081,6 +1083,24 @@ namespace Ryujinx.Graphics.Shader.Instructions SamplerType type = ConvertSamplerType(op.Dimensions); + bool hasLod = op.LodMode > TextureLodMode.LodZero; + + if (type == SamplerType.Texture1D && (flags & ~TextureFlags.Bindless) == TextureFlags.IntCoords && !(hasLod || + op.HasDepthCompare || + op.HasOffset || + op.IsArray || + op.IsMultisample)) + { + // For bindless, we don't have any way to know the texture type, + // so we assume it's texture buffer when the sampler type is 1D, since that's more common. + bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QueryIsTextureBuffer(op.Immediate); + + if (isTypeBuffer) + { + type = SamplerType.TextureBuffer; + } + } + int coordsCount = type.GetDimensions(); for (int index = 0; index < coordsCount; index++) @@ -1095,8 +1115,6 @@ namespace Ryujinx.Graphics.Shader.Instructions type |= SamplerType.Array; } - bool hasLod = op.LodMode > TextureLodMode.LodZero; - Operand lodValue = hasLod ? Rb() : ConstF(0); Operand packedOffs = op.HasOffset ? Rb() : null; @@ -1110,7 +1128,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if ((op.LodMode == TextureLodMode.LodZero || op.LodMode == TextureLodMode.LodLevel || - op.LodMode == TextureLodMode.LodLevelA) && !op.IsMultisample) + op.LodMode == TextureLodMode.LodLevelA) && !op.IsMultisample && type != SamplerType.TextureBuffer) { sourcesList.Add(lodValue); @@ -1142,16 +1160,6 @@ namespace Ryujinx.Graphics.Shader.Instructions type |= SamplerType.Multisample; } - if (type == SamplerType.Texture1D && flags == TextureFlags.IntCoords && !isBindless) - { - bool isTypeBuffer = context.Config.GpuAccessor.QueryIsTextureBuffer(op.Immediate); - - if (isTypeBuffer) - { - type = SamplerType.TextureBuffer; - } - } - Operand[] sources = sourcesList.ToArray(); int rdIndex = op.Rd.Index; diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 6b7fb82f..2c4a88cd 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { class Operation : INode @@ -78,6 +80,18 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation _sources[index] = source; } + protected void RemoveSource(int index) + { + SetSource(index, null); + + Operand[] newSources = new Operand[_sources.Length - 1]; + + Array.Copy(_sources, 0, newSources, 0, index); + Array.Copy(_sources, index + 1, newSources, index, _sources.Length - (index + 1)); + + _sources = newSources; + } + public void TurnIntoCopy(Operand source) { TurnInto(Instruction.Copy, source); diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 06541f90..9c5cd25c 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -26,8 +26,18 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public void TurnIntoIndexed(int handle) { Type |= SamplerType.Indexed; - Flags &= ~TextureFlags.Bindless; + Handle = handle; + } + + public void SetHandle(int handle) + { + if ((Flags & TextureFlags.Bindless) != 0) + { + Flags &= ~TextureFlags.Bindless; + + RemoveSource(0); + } Handle = handle; } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index a3fa3e3a..bb935ce7 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr class AstTextureOperation : AstOperation { public SamplerType Type { get; } - public TextureFormat Format { get; } + public TextureFormat Format { get; set; } public TextureFlags Flags { get; } public int Handle { get; } diff --git a/Ryujinx.Graphics.Shader/Translation/Lowering.cs b/Ryujinx.Graphics.Shader/Translation/Lowering.cs index 6f52ce96..6da25983 100644 --- a/Ryujinx.Graphics.Shader/Translation/Lowering.cs +++ b/Ryujinx.Graphics.Shader/Translation/Lowering.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.Translation.GlobalMemory; @@ -27,9 +28,17 @@ namespace Ryujinx.Graphics.Shader.Translation node = RewriteGlobalAccess(node, config); } - if (operation.Inst == Instruction.TextureSample) + if (operation is TextureOperation texOp) { - node = RewriteTextureSample(node, config); + if (texOp.Inst == Instruction.TextureSample) + { + node = RewriteTextureSample(node, config); + } + + if (texOp.Type == SamplerType.TextureBuffer) + { + node = InsertSnormNormalization(node, config); + } } } } @@ -419,5 +428,57 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } + + private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config) + { + TextureOperation texOp = (TextureOperation)node.Value; + + TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle); + + int maxPositive = format switch + { + TextureFormat.R8Snorm => sbyte.MaxValue, + TextureFormat.R8G8Snorm => sbyte.MaxValue, + TextureFormat.R8G8B8A8Snorm => sbyte.MaxValue, + TextureFormat.R16Snorm => short.MaxValue, + TextureFormat.R16G16Snorm => short.MaxValue, + TextureFormat.R16G16B16A16Snorm => short.MaxValue, + _ => 0 + }; + + // The value being 0 means that the format is not a SNORM format, so there's nothing to do here. + if (maxPositive == 0) + { + return node; + } + + // Do normalization. We assume SINT formats are being used as replacement for SNORM (that is not supported). + INode[] uses = texOp.Dest.UseOps.ToArray(); + + Operation convOp = new Operation(Instruction.ConvertS32ToFP, Local(), texOp.Dest); + Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); + + node = node.List.AddAfter(node, convOp); + node = node.List.AddAfter(node, normOp); + + foreach (INode useOp in uses) + { + if (!(useOp is Operation op)) + { + continue; + } + + // Replace all uses of the texture pixel value with the normalized value. + for (int index = 0; index < op.SourcesCount; index++) + { + if (op.GetSource(index) == texOp.Dest) + { + op.SetSource(index, normOp.Dest); + } + } + } + + return node; + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs new file mode 100644 index 00000000..9515c349 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -0,0 +1,50 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + class BindlessElimination + { + public static void RunPass(BasicBlock block) + { + // We can turn a bindless into regular access by recognizing the pattern + // produced by the compiler for separate texture and sampler. + // We check for the following conditions: + // - The handle is the result of a bitwise OR logical operation. + // - Both sources of the OR operation comes from CB2 (used by NVN to hold texture handles). + for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) + { + if (!(node.Value is TextureOperation texOp)) + { + continue; + } + + if ((texOp.Flags & TextureFlags.Bindless) == 0) + { + continue; + } + + if (!(texOp.GetSource(0).AsgOp is Operation handleCombineOp)) + { + continue; + } + + if (handleCombineOp.Inst != Instruction.BitwiseOr) + { + continue; + } + + Operand src0 = handleCombineOp.GetSource(0); + Operand src1 = handleCombineOp.GetSource(1); + + if (src0.Type != OperandType.ConstantBuffer || src0.GetCbufSlot() != 2 || + src1.Type != OperandType.ConstantBuffer || src1.GetCbufSlot() != 2) + { + continue; + } + + texOp.SetHandle(src0.GetCbufOffset() | (src1.GetCbufOffset() << 16)); + } + } + } +} diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index c5db4678..10a0e780 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -88,6 +89,22 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { BindlessToIndexed.RunPass(blocks[blkIndex]); + BindlessElimination.RunPass(blocks[blkIndex]); + + // Try to eliminate any operations that are now unused. + LinkedListNode<INode> node = blocks[blkIndex].Operations.First; + + while (node != null) + { + LinkedListNode<INode> nextNode = node.Next; + + if (IsUnused(node.Value)) + { + RemoveNode(blocks[blkIndex], node); + } + + node = nextNode; + } } } |
