aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Shader/Translation
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2023-05-25 17:46:58 -0300
committerGitHub <noreply@github.com>2023-05-25 17:46:58 -0300
commit8f0c89ffd65eb2b979b24d457708218050fec6ae (patch)
tree5657b8a7f6246659d97f0f574f3f1829d65a235e /src/Ryujinx.Graphics.Shader/Translation
parent2c9715acf6cb9ae8bceabec5e81602c7d64c7192 (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')
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs10
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs6
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs134
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs11
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs443
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs30
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs8
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Translator.cs12
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);