aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Shader/Translation/Rewriter.cs')
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Rewriter.cs258
1 files changed, 167 insertions, 91 deletions
diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index 0c3c4a57..3ec4e49a 100644
--- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -385,15 +385,6 @@ namespace Ryujinx.Graphics.Shader.Translation
int componentIndex = texOp.Index;
- Operand Int(Operand value)
- {
- Operand res = Local();
-
- node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
-
- return res;
- }
-
Operand Float(Operand value)
{
Operand res = Local();
@@ -436,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.CbufSlot,
texOp.Handle,
index,
- coordSize,
+ new[] { coordSize },
texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
@@ -451,124 +442,204 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
+ Operand[] dests = new Operand[texOp.DestsCount];
+
+ for (int i = 0; i < texOp.DestsCount; i++)
+ {
+ dests[i] = texOp.GetDest(i);
+ }
+
+ Operand bindlessHandle = isBindless || isIndexed ? sources[0] : null;
+
+ 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 take advantage of the fact that the operation is already broken down
- // to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset
- // for each pixel.
- if (hasInvalidOffset)
+ // 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 (intCoords)
+ config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ Operand[] newSources = new Operand[sources.Length];
+
+ sources.CopyTo(newSources, 0);
+
+ Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
+
+ int destIndex = 0;
+
+ for (int compIndex = 0; compIndex < 4; compIndex++)
{
+ if (((texOp.Index >> compIndex) & 1) == 0)
+ {
+ continue;
+ }
+
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 intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
+
+ node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
+
Operand source = sources[coordsIndex + index];
Operand coordPlusOffset = Local();
- node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
+ node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
- sources[coordsIndex + index] = coordPlusOffset;
+ newSources[coordsIndex + index] = coordPlusOffset;
}
- }
- else
- {
- config.SetUsedFeature(FeatureFlags.IntegerSampling);
-
- Operand lod = Local();
- node.List.AddBefore(node, new TextureOperation(
- Instruction.Lod,
+ TextureOperation newTexOp = new TextureOperation(
+ Instruction.TextureSample,
texOp.Type,
texOp.Format,
- texOp.Flags,
+ texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot,
texOp.Handle,
- 0,
- lod,
- lodSources));
+ 1,
+ new[] { dests[destIndex++] },
+ newSources);
- for (int index = 0; index < coordsCount; index++)
+ node = node.List.AddBefore(node, newTexOp);
+ }
+ }
+ else
+ {
+ if (hasInvalidOffset)
+ {
+ if (intCoords)
{
- Operand coordSize = Local();
+ for (int index = 0; index < coordsCount; index++)
+ {
+ Operand source = sources[coordsIndex + index];
- Operand[] texSizeSources;
+ Operand coordPlusOffset = Local();
- if (isBindless || isIndexed)
- {
- texSizeSources = new Operand[] { sources[0], Int(lod) };
- }
- else
- {
- texSizeSources = new Operand[] { Int(lod) };
+ node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
+
+ sources[coordsIndex + index] = coordPlusOffset;
}
+ }
+ else
+ {
+ config.SetUsedFeature(FeatureFlags.IntegerSampling);
- node.List.AddBefore(node, new TextureOperation(
- Instruction.TextureSize,
- texOp.Type,
- texOp.Format,
- texOp.Flags,
- texOp.CbufSlot,
- texOp.Handle,
- index,
- coordSize,
- texSizeSources));
+ Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
- 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 + (hasOffsets ? texOp.Index * coordsCount : 0)];
+ Operand intOffset = offsets[index];
- node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(coordSize)));
+ node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(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;
+ }
}
}
- if (isGather && !isShadow)
- {
- Operand gatherComponent = sources[dstIndex - 1];
+ TextureOperation newTexOp = new TextureOperation(
+ Instruction.TextureSample,
+ texOp.Type,
+ texOp.Format,
+ texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
+ texOp.CbufSlot,
+ texOp.Handle,
+ componentIndex,
+ dests,
+ sources);
- Debug.Assert(gatherComponent.Type == OperandType.Constant);
+ node = node.List.AddBefore(node, newTexOp);
+ }
- componentIndex = gatherComponent.Value;
- }
+ node.List.Remove(oldNode);
+
+ for (int index = 0; index < texOp.SourcesCount; index++)
+ {
+ texOp.SetSource(index, null);
+ }
+
+ return node;
+ }
+
+ private static Operand[] InsertTextureSize(
+ 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;
}
- TextureOperation newTexOp = new TextureOperation(
- Instruction.TextureSample,
+ Operand[] texSizes = new Operand[coordsCount];
+
+ Operand lod = Local();
+
+ node.List.AddBefore(node, new TextureOperation(
+ Instruction.Lod,
texOp.Type,
texOp.Format,
- texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
+ texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
- componentIndex,
- texOp.Dest,
- sources);
+ 0,
+ new[] { lod },
+ lodSources));
- for (int index = 0; index < texOp.SourcesCount; index++)
+ for (int index = 0; index < coordsCount; index++)
{
- texOp.SetSource(index, null);
- }
+ texSizes[index] = Local();
- LinkedListNode<INode> oldNode = node;
+ Operand[] texSizeSources;
- node = node.List.AddBefore(node, newTexOp);
+ if (bindlessHandle != null)
+ {
+ texSizeSources = new Operand[] { bindlessHandle, Int(lod) };
+ }
+ else
+ {
+ texSizeSources = new Operand[] { Int(lod) };
+ }
- node.List.Remove(oldNode);
+ node.List.AddBefore(node, new TextureOperation(
+ Instruction.TextureSize,
+ texOp.Type,
+ texOp.Format,
+ texOp.Flags,
+ texOp.CbufSlot,
+ texOp.Handle,
+ index,
+ new[] { texSizes[index] },
+ texSizeSources));
+ }
- return node;
+ return texSizes;
}
private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config)
@@ -604,27 +675,32 @@ namespace Ryujinx.Graphics.Shader.Translation
// Do normalization. We assume SINT formats are being used
// as replacement for SNORM (which is not supported).
- INode[] uses = texOp.Dest.UseOps.ToArray();
+ for (int i = 0; i < texOp.DestsCount; i++)
+ {
+ Operand dest = texOp.GetDest(i);
- Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), texOp.Dest);
- Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive));
+ INode[] uses = dest.UseOps.ToArray();
- node = node.List.AddAfter(node, convOp);
- node = node.List.AddAfter(node, normOp);
+ Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), dest);
+ Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive));
- foreach (INode useOp in uses)
- {
- if (useOp is not Operation op)
- {
- continue;
- }
+ node = node.List.AddAfter(node, convOp);
+ node = node.List.AddAfter(node, normOp);
- // Replace all uses of the texture pixel value with the normalized value.
- for (int index = 0; index < op.SourcesCount; index++)
+ foreach (INode useOp in uses)
{
- if (op.GetSource(index) == texOp.Dest)
+ if (useOp is not Operation op)
{
- op.SetSource(index, normOp.Dest);
+ 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) == dest)
+ {
+ op.SetSource(index, normOp.Dest);
+ }
}
}
}