diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2023-05-25 17:46:58 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-25 17:46:58 -0300 |
| commit | 8f0c89ffd65eb2b979b24d457708218050fec6ae (patch) | |
| tree | 5657b8a7f6246659d97f0f574f3f1829d65a235e /src/Ryujinx.Graphics.Shader/Translation | |
| parent | 2c9715acf6cb9ae8bceabec5e81602c7d64c7192 (diff) | |
Generate scaling helper functions on IR (#4714)
* Generate scaling helper functions on IR
* Delete unused code
* Split RewriteTextureSample and move gather bias add to an earlier pass
* Remove using
* Shader cache version bump
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation')
8 files changed, 527 insertions, 127 deletions
diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 0c51b16f..2786caaa 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -49,13 +49,17 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly List<Operation> _operations; private readonly Dictionary<ulong, BlockLabel> _labels; - public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) + public EmitterContext() + { + _operations = new List<Operation>(); + _labels = new Dictionary<ulong, BlockLabel>(); + } + + public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this() { Program = program; Config = config; IsNonMain = isNonMain; - _operations = new List<Operation>(); - _labels = new Dictionary<ulong, BlockLabel>(); EmitStart(); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index e41a28f1..6d4104ce 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -307,6 +307,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.Minimum, Local(), a, b); } + public static Operand FPModulo(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) + { + return context.Add(fpType | Instruction.Modulo, Local(), a, b); + } + public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.Multiply, Local(), a, b); @@ -656,7 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation public static void Return(this EmitterContext context, Operand returnValue) { - context.PrepareForReturn(); context.Add(Instruction.Return, null, returnValue); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs new file mode 100644 index 00000000..206facd4 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -0,0 +1,134 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class HelperFunctionManager + { + private readonly List<Function> _functionList; + private readonly Dictionary<HelperFunctionName, int> _functionIds; + private readonly ShaderStage _stage; + + public HelperFunctionManager(List<Function> functionList, ShaderStage stage) + { + _functionList = functionList; + _functionIds = new Dictionary<HelperFunctionName, int>(); + _stage = stage; + } + + public int GetOrCreateFunctionId(HelperFunctionName functionName) + { + if (_functionIds.TryGetValue(functionName, out int functionId)) + { + return functionId; + } + + Function function = GenerateFunction(functionName); + functionId = _functionList.Count; + _functionList.Add(function); + _functionIds.Add(functionName, functionId); + + return functionId; + } + + private Function GenerateFunction(HelperFunctionName functionName) + { + return functionName switch + { + HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(), + HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(), + _ => throw new ArgumentException($"Invalid function name {functionName}") + }; + } + + private Function GenerateTexelFetchScaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + int inArgumentsCount; + + if (_stage == ShaderStage.Fragment) + { + Operand scaleIsLessThanZero = context.FPCompareLess(scale, ConstF(0f)); + Operand lblScaleGreaterOrEqualZero = Label(); + + context.BranchIfFalse(lblScaleGreaterOrEqualZero, scaleIsLessThanZero); + + Operand negScale = context.FPNegate(scale); + Operand inputScaled = context.FPMultiply(context.IConvertS32ToFP32(input), negScale); + Operand fragCoordX = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0)); + Operand fragCoordY = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1)); + Operand fragCoord = context.ConditionalSelect(Argument(2), fragCoordY, fragCoordX); + Operand inputBias = context.FPModulo(fragCoord, negScale); + Operand inputWithBias = context.FPAdd(inputScaled, inputBias); + + context.Return(context.FP32ConvertToS32(inputWithBias)); + context.MarkLabel(lblScaleGreaterOrEqualZero); + + inArgumentsCount = 3; + } + else + { + inArgumentsCount = 2; + } + + Operand inputScaled2 = context.FPMultiply(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputScaled2)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TexelFetchScale", true, inArgumentsCount, 0); + } + + private Function GenerateTextureSizeUnscaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.FPAbsolute(context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index)); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + Operand inputUnscaled = context.FPDivide(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputUnscaled)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TextureSizeUnscale", true, 2, 0); + } + + private Operand GetScaleIndex(EmitterContext context, Operand index) + { + switch (_stage) + { + case ShaderStage.Vertex: + Operand fragScaleCount = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.FragmentRenderScaleCount)); + return context.IAdd(Const(1), context.IAdd(index, fragScaleCount)); + default: + return context.IAdd(Const(1), index); + } + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs new file mode 100644 index 00000000..5accdf65 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation +{ + enum HelperFunctionName + { + TexelFetchScale, + TextureSizeUnscale + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 711661c9..6b55a484 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation { static class Rewriter { - public static void RunPass(BasicBlock[] blocks, ShaderConfig config) + public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { bool isVertexShader = config.Stage == ShaderStage.Vertex; bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); @@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Shader.Translation if (operation is TextureOperation texOp) { + node = InsertTexelFetchScale(hfm, node, config); + node = InsertTextureSizeUnscale(hfm, node, config); + if (texOp.Inst == Instruction.TextureSample) { - node = RewriteTextureSample(node, config); + node = InsertCoordNormalization(node, config); + node = InsertCoordGatherBias(node, config); + node = InsertConstOffsets(node, config); if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat) { @@ -344,8 +349,277 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + { + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + bool isImage = IsImageInstructionWithScale(texOp.Inst); + + if ((texOp.Inst == Instruction.TextureSample || isImage) && + intCoords && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); + int samplerIndex = isImage + ? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) + : config.FindTextureDescriptorIndex(texOp); + + for (int index = 0; index < coordsCount; index++) + { + Operand scaledCoord = Local(); + Operand[] callArgs; + + if (config.Stage == ShaderStage.Fragment) + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) }; + } + else + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex) }; + } + + node.List.AddBefore(node, new Operation(Instruction.Call, 0, scaledCoord, callArgs)); + + texOp.SetSource(coordsIndex + index, scaledCoord); + } + } + + return node; + } + + private static LinkedListNode<INode> InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + { + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + if (texOp.Inst == Instruction.TextureSize && + texOp.Index < 2 && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); + int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true); + + for (int index = texOp.DestsCount - 1; index >= 0; index--) + { + Operand dest = texOp.GetDest(index); + + Operand unscaledSize = Local(); + + // Replace all uses with the unscaled size value. + // This must be done before the call is added, since it also is a use of the original size. + foreach (INode useOp in dest.UseOps) + { + for (int srcIndex = 0; srcIndex < useOp.SourcesCount; srcIndex++) + { + if (useOp.GetSource(srcIndex) == dest) + { + useOp.SetSource(srcIndex, unscaledSize); + } + } + } + + Operand[] callArgs = new Operand[] { Const(functionId), dest, Const(samplerIndex) }; + + node.List.AddAfter(node, new Operation(Instruction.Call, 0, unscaledSize, callArgs)); + } + } + + return node; + } + + private static bool IsImageInstructionWithScale(Instruction inst) + { + // Currently, we don't support scaling images that are modified, + // so we only need to care about the load instruction. + return inst == Instruction.ImageLoad; + } + + private static bool TypeSupportsScale(SamplerType type) + { + return (type & SamplerType.Mask) == SamplerType.Texture2D; + } + + private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config) + { + // Emulate non-normalized coordinates by normalizing the coordinates on the shader. + // Without normalization, the coordinates are expected to the in the [0, W or H] range, + // and otherwise, it is expected to be in the [0, 1] range. + // We normalize by dividing the coords by the texture size. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); + + if (isCoordNormalized || intCoords) + { + return node; + } + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordNormalized = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); + + texOp.SetSource(coordsIndex + index, coordNormalized); + } + + return node; + } + + private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ShaderConfig config) + { + // The gather behavior when the coordinate sits right in the middle of two texels is not well defined. + // To ensure the correct texel is sampled, we add a small bias value to the coordinate. + // This value is calculated as the minimum value required to change the texel it will sample from, + // and is 0 if the host does not require the bias. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + + int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision(); + + if (!isGather || gatherBiasPrecision == 0) + { + return node; + } + + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + Operand scaledSize = Local(); + Operand bias = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Multiply, + scaledSize, + GenerateI2f(node, coordSize), + ConstF((float)(1 << (gatherBiasPrecision + 1))))); + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, bias, ConstF(1f), scaledSize)); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordBiased = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordBiased, source, bias)); + + texOp.SetSource(coordsIndex + index, coordBiased); + } + + return node; + } + + private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ShaderConfig config) { + // Non-constant texture offsets are not allowed (according to the spec), + // however some GPUs does support that. + // For GPUs where it is not supported, we can replace the instruction with the following: + // For texture*Offset, we replace it by texture*, and add the offset to the P coords. + // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). + // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. + // For textureGatherOffset, we split the operation into up to 4 operations, one for each component + // that is accessed, where each textureGather operation has a different offset for each pixel. + TextureOperation texOp = (TextureOperation)node.Value; bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; @@ -355,9 +629,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); - - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -454,7 +726,7 @@ namespace Ryujinx.Graphics.Shader.Translation hasInvalidOffset &= !areAllOffsetsConstant; - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -473,63 +745,6 @@ namespace Ryujinx.Graphics.Shader.Translation int componentIndex = texOp.Index; - Operand Float(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); - - return res; - } - - // Emulate non-normalized coordinates by normalizing the coordinates on the shader. - // Without normalization, the coordinates are expected to the in the [0, W or H] range, - // and otherwise, it is expected to be in the [0, 1] range. - // We normalize by dividing the coords by the texture size. - if (!isCoordNormalized && !intCoords) - { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - - int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; - - for (int index = 0; index < normCoordsCount; index++) - { - Operand coordSize = Local(); - - Operand[] texSizeSources; - - if (isBindless || isIndexed) - { - texSizeSources = new Operand[] { sources[0], Const(0) }; - } - else - { - texSizeSources = new Operand[] { Const(0) }; - } - - node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - index, - new[] { coordSize }, - texSizeSources)); - - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - - Operand source = sources[coordsIndex + index]; - - Operand coordNormalized = Local(); - - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize))); - - sources[coordsIndex + index] = coordNormalized; - } - } - Operand[] dests = new Operand[texOp.DestsCount]; for (int i = 0; i < texOp.DestsCount; i++) @@ -541,15 +756,7 @@ namespace Ryujinx.Graphics.Shader.Translation LinkedListNode<INode> oldNode = node; - // Technically, non-constant texture offsets are not allowed (according to the spec), - // however some GPUs does support that. - // For GPUs where it is not supported, we can replace the instruction with the following: - // For texture*Offset, we replace it by texture*, and add the offset to the P coords. - // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). - // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. - // For textureGatherOffset, we split the operation into up to 4 operations, one for each component - // that is accessed, where each textureGather operation has a different offset for each pixel. - if (hasInvalidOffset && isGather && !isShadow) + if (isGather && !isShadow) { config.SetUsedFeature(FeatureFlags.IntegerSampling); @@ -557,7 +764,7 @@ namespace Ryujinx.Graphics.Shader.Translation sources.CopyTo(newSources, 0); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); int destIndex = 0; @@ -576,7 +783,11 @@ namespace Ryujinx.Graphics.Shader.Translation Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); Operand source = sources[coordsIndex + index]; @@ -603,45 +814,46 @@ namespace Ryujinx.Graphics.Shader.Translation } else { - if (hasInvalidOffset) + if (intCoords) { - if (intCoords) + for (int index = 0; index < coordsCount; index++) { - for (int index = 0; index < coordsCount; index++) - { - Operand source = sources[coordsIndex + index]; + Operand source = sources[coordsIndex + index]; - Operand coordPlusOffset = Local(); + Operand coordPlusOffset = Local(); - node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); + node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } - else - { - config.SetUsedFeature(FeatureFlags.IntegerSampling); + } + else + { + config.SetUsedFeature(FeatureFlags.IntegerSampling); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); - for (int index = 0; index < coordsCount; index++) - { - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + for (int index = 0; index < coordsCount; index++) + { + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - Operand offset = Local(); + Operand offset = Local(); - Operand intOffset = offsets[index]; + Operand intOffset = offsets[index]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); - Operand source = sources[coordsIndex + index]; + Operand source = sources[coordsIndex + index]; - Operand coordPlusOffset = Local(); + Operand coordPlusOffset = Local(); - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } } @@ -669,22 +881,13 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static Operand[] InsertTextureSize( + private static Operand[] InsertTextureLod( LinkedListNode<INode> node, TextureOperation texOp, Operand[] lodSources, Operand bindlessHandle, int coordsCount) { - Operand Int(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); - - return res; - } - Operand[] texSizes = new Operand[coordsCount]; Operand lod = Local(); @@ -708,11 +911,11 @@ namespace Ryujinx.Graphics.Shader.Translation if (bindlessHandle != null) { - texSizeSources = new Operand[] { bindlessHandle, Int(lod) }; + texSizeSources = new Operand[] { bindlessHandle, GenerateF2i(node, lod) }; } else { - texSizeSources = new Operand[] { Int(lod) }; + texSizeSources = new Operand[] { GenerateF2i(node, lod) }; } node.List.AddBefore(node, new TextureOperation( @@ -796,6 +999,24 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } + private static Operand GenerateI2f(LinkedListNode<INode> node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); + + return res; + } + + private static Operand GenerateF2i(LinkedListNode<INode> node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); + + return res; + } + private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation) { Operand GenerateLoad(IoVariable ioVariable) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 77560797..73525cb2 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -860,7 +860,7 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } - public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp) + public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp) { TextureDescriptor[] descriptors = GetTextureDescriptors(); @@ -872,11 +872,11 @@ namespace Ryujinx.Graphics.Shader.Translation descriptor.HandleIndex == texOp.Handle && descriptor.Format == texOp.Format) { - return (descriptor, i); + return descriptor; } } - return (default, -1); + return default; } private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp) @@ -897,12 +897,30 @@ namespace Ryujinx.Graphics.Shader.Translation return -1; } - public int FindTextureDescriptorIndex(AstTextureOperation texOp) + private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false) { - return FindDescriptorIndex(GetTextureDescriptors(), texOp); + for (int i = 0; i < array.Length; i++) + { + var descriptor = array[i]; + + if ((descriptor.Type == texOp.Type || ignoreType) && + descriptor.CbufSlot == texOp.CbufSlot && + descriptor.HandleIndex == texOp.Handle && + descriptor.Format == texOp.Format) + { + return i; + } + } + + return -1; + } + + public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false) + { + return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType); } - public int FindImageDescriptorIndex(AstTextureOperation texOp) + public int FindImageDescriptorIndex(TextureOperation texOp) { return FindDescriptorIndex(GetImageDescriptors(), texOp); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 53f1e847..867e2437 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -1,11 +1,11 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; +using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation { static class ShaderIdentifier { - public static ShaderIdentification Identify(Function[] functions, ShaderConfig config) + public static ShaderIdentification Identify(IReadOnlyList<Function> functions, ShaderConfig config) { if (config.Stage == ShaderStage.Geometry && config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && @@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation return ShaderIdentification.None; } - private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr) + private static bool IsLayerPassthroughGeometryShader(IReadOnlyList<Function> functions, out int layerInputAttr) { bool writesLayer = false; layerInputAttr = 0; - if (functions.Length != 1) + if (functions.Count != 1) { return false; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 87d97e52..5bbc0009 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation.Optimizations; using System; +using System.Collections.Generic; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -44,7 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation } } - Function[] funcs = new Function[functions.Length]; + List<Function> funcs = new List<Function>(functions.Length); + + for (int i = 0; i < functions.Length; i++) + { + funcs.Add(null); + } + + HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage); for (int i = 0; i < functions.Length; i++) { @@ -71,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(cfg.Blocks); Optimizer.RunPass(cfg.Blocks, config); - Rewriter.RunPass(cfg.Blocks, config); + Rewriter.RunPass(hfm, cfg.Blocks, config); } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); |
