Better IPA shader instruction implementation (#1082)
* Fix varying interpolation on fragment shader * Some nits * Alignment
This commit is contained in:
parent
2365ddfc36
commit
e93ca84b14
13 changed files with 97 additions and 89 deletions
|
@ -365,11 +365,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
foreach (int attr in info.IAttributes.OrderBy(x => x))
|
foreach (int attr in info.IAttributes.OrderBy(x => x))
|
||||||
{
|
{
|
||||||
string iq = info.InterpolationQualifiers[attr].ToGlslQualifier();
|
string iq = string.Empty;
|
||||||
|
|
||||||
if (iq != string.Empty)
|
if (context.Config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
iq += " ";
|
iq = context.Config.ImapTypes[attr].GetFirstUsedType() switch
|
||||||
|
{
|
||||||
|
PixelImap.Constant => "flat ",
|
||||||
|
PixelImap.ScreenLinear => "noperspective ",
|
||||||
|
_ => string.Empty
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AppendLine($"layout (location = {attr}) {iq}in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};");
|
context.AppendLine($"layout (location = {attr}) {iq}in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};");
|
||||||
|
|
|
@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
case AttributeConsts.PositionX: return "gl_FragCoord.x";
|
case AttributeConsts.PositionX: return "gl_FragCoord.x";
|
||||||
case AttributeConsts.PositionY: return "gl_FragCoord.y";
|
case AttributeConsts.PositionY: return "gl_FragCoord.y";
|
||||||
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
||||||
case AttributeConsts.PositionW: return "1.0";
|
case AttributeConsts.PositionW: return "gl_FragCoord.w";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,17 +96,27 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
OpCodeIpa op = (OpCodeIpa)context.CurrOp;
|
OpCodeIpa op = (OpCodeIpa)context.CurrOp;
|
||||||
|
|
||||||
InterpolationQualifier iq = InterpolationQualifier.None;
|
Operand res = Attribute(op.AttributeOffset);
|
||||||
|
|
||||||
switch (op.Mode)
|
if (op.AttributeOffset >= AttributeConsts.UserAttributeBase &&
|
||||||
|
op.AttributeOffset < AttributeConsts.UserAttributeEnd)
|
||||||
{
|
{
|
||||||
case InterpolationMode.Constant: iq = InterpolationQualifier.Flat; break;
|
int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4;
|
||||||
case InterpolationMode.Pass: iq = InterpolationQualifier.NoPerspective; break;
|
|
||||||
|
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
|
||||||
|
{
|
||||||
|
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op.Mode == InterpolationMode.Default)
|
||||||
|
{
|
||||||
|
Operand srcB = GetSrcB(context);
|
||||||
|
|
||||||
|
res = context.FPMultiply(res, srcB);
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand srcA = Attribute(op.AttributeOffset, iq);
|
res = context.FPSaturate(res, op.Saturate);
|
||||||
|
|
||||||
Operand res = context.FPSaturate(srcA, op.Saturate);
|
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
|
||||||
public int Value { get; }
|
public int Value { get; }
|
||||||
|
|
||||||
public InterpolationQualifier Interpolation { get; }
|
|
||||||
|
|
||||||
public INode AsgOp { get; set; }
|
public INode AsgOp { get; set; }
|
||||||
|
|
||||||
public HashSet<INode> UseOps { get; }
|
public HashSet<INode> UseOps { get; }
|
||||||
|
@ -30,11 +28,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
Type = type;
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand(OperandType type, int value, InterpolationQualifier iq = InterpolationQualifier.None) : this()
|
public Operand(OperandType type, int value) : this()
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Value = value;
|
Value = value;
|
||||||
Interpolation = iq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand(Register reg) : this()
|
public Operand(Register reg) : this()
|
||||||
|
|
|
@ -5,9 +5,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
static class OperandHelper
|
static class OperandHelper
|
||||||
{
|
{
|
||||||
public static Operand Attribute(int value, InterpolationQualifier iq = InterpolationQualifier.None)
|
public static Operand Attribute(int value)
|
||||||
{
|
{
|
||||||
return new Operand(OperandType.Attribute, value, iq);
|
return new Operand(OperandType.Attribute, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Cbuf(int slot, int offset)
|
public static Operand Cbuf(int slot, int offset)
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
enum InterpolationQualifier
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
Flat = 1,
|
|
||||||
NoPerspective = 2,
|
|
||||||
Smooth = 3,
|
|
||||||
|
|
||||||
Centroid = 1 << 16,
|
|
||||||
Sample = 1 << 17,
|
|
||||||
|
|
||||||
FlagsMask = Centroid | Sample
|
|
||||||
}
|
|
||||||
|
|
||||||
static class InterpolationQualifierExtensions
|
|
||||||
{
|
|
||||||
public static string ToGlslQualifier(this InterpolationQualifier iq)
|
|
||||||
{
|
|
||||||
string output = (iq & ~InterpolationQualifier.FlagsMask) switch
|
|
||||||
{
|
|
||||||
InterpolationQualifier.Flat => "flat",
|
|
||||||
InterpolationQualifier.NoPerspective => "noperspective",
|
|
||||||
InterpolationQualifier.Smooth => "smooth",
|
|
||||||
_ => string.Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((iq & InterpolationQualifier.Centroid) != 0)
|
|
||||||
{
|
|
||||||
output = "centroid " + output;
|
|
||||||
}
|
|
||||||
else if ((iq & InterpolationQualifier.Sample) != 0)
|
|
||||||
{
|
|
||||||
output = "sample " + output;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,10 +24,5 @@ namespace Ryujinx.Graphics.Shader
|
||||||
{
|
{
|
||||||
Code = line + Environment.NewLine + Code;
|
Code = line + Environment.NewLine + Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Replace(string name, string value)
|
|
||||||
{
|
|
||||||
Code = Code.Replace(name, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,8 +12,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
public VariableType VarType { get; set; }
|
public VariableType VarType { get; set; }
|
||||||
|
|
||||||
public InterpolationQualifier Interpolation { get; }
|
|
||||||
|
|
||||||
public int Value { get; }
|
public int Value { get; }
|
||||||
|
|
||||||
public int CbufSlot { get; }
|
public int CbufSlot { get; }
|
||||||
|
@ -29,8 +27,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
public AstOperand(Operand operand) : this()
|
public AstOperand(Operand operand) : this()
|
||||||
{
|
{
|
||||||
Type = operand.Type;
|
Type = operand.Type;
|
||||||
Interpolation = operand.Interpolation;
|
|
||||||
|
|
||||||
if (Type == OperandType.ConstantBuffer)
|
if (Type == OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
|
|
|
@ -273,8 +273,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
if (TryGetUserAttributeIndex(operand, out int attrIndex))
|
if (TryGetUserAttributeIndex(operand, out int attrIndex))
|
||||||
{
|
{
|
||||||
Info.IAttributes.Add(attrIndex);
|
Info.IAttributes.Add(attrIndex);
|
||||||
|
|
||||||
Info.InterpolationQualifiers[attrIndex] = operand.Interpolation;
|
|
||||||
}
|
}
|
||||||
else if (operand.Type == OperandType.Attribute && operand.Value == AttributeConsts.InstanceId)
|
else if (operand.Type == OperandType.Attribute && operand.Value == AttributeConsts.InstanceId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,8 +14,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
public HashSet<int> IAttributes { get; }
|
public HashSet<int> IAttributes { get; }
|
||||||
public HashSet<int> OAttributes { get; }
|
public HashSet<int> OAttributes { get; }
|
||||||
|
|
||||||
public InterpolationQualifier[] InterpolationQualifiers { get; }
|
|
||||||
|
|
||||||
public bool UsesInstanceId { get; set; }
|
public bool UsesInstanceId { get; set; }
|
||||||
|
|
||||||
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
||||||
|
@ -35,8 +33,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
IAttributes = new HashSet<int>();
|
IAttributes = new HashSet<int>();
|
||||||
OAttributes = new HashSet<int>();
|
OAttributes = new HashSet<int>();
|
||||||
|
|
||||||
InterpolationQualifiers = new InterpolationQualifier[32];
|
|
||||||
|
|
||||||
Samplers = new HashSet<AstTextureOperation>();
|
Samplers = new HashSet<AstTextureOperation>();
|
||||||
Images = new HashSet<AstTextureOperation>();
|
Images = new HashSet<AstTextureOperation>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
for (int attachment = 0; attachment < 8; attachment++)
|
for (int attachment = 0; attachment < 8; attachment++)
|
||||||
{
|
{
|
||||||
OutputMapTarget target = _config.OmapTargets[attachment];
|
OmapTarget target = _config.OmapTargets[attachment];
|
||||||
|
|
||||||
for (int component = 0; component < 4; component++)
|
for (int component = 0; component < 4; component++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,9 +12,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public int LocalMemorySize { get; }
|
public int LocalMemorySize { get; }
|
||||||
|
|
||||||
public OutputMapTarget[] OmapTargets { get; }
|
public ImapPixelType[] ImapTypes { get; }
|
||||||
public bool OmapSampleMask { get; }
|
|
||||||
public bool OmapDepth { get; }
|
public OmapTarget[] OmapTargets { get; }
|
||||||
|
public bool OmapSampleMask { get; }
|
||||||
|
public bool OmapDepth { get; }
|
||||||
|
|
||||||
public TranslationFlags Flags { get; }
|
public TranslationFlags Flags { get; }
|
||||||
|
|
||||||
|
@ -26,6 +28,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
OutputTopology = OutputTopology.PointList;
|
OutputTopology = OutputTopology.PointList;
|
||||||
MaxOutputVertices = 0;
|
MaxOutputVertices = 0;
|
||||||
LocalMemorySize = 0;
|
LocalMemorySize = 0;
|
||||||
|
ImapTypes = null;
|
||||||
OmapTargets = null;
|
OmapTargets = null;
|
||||||
OmapSampleMask = false;
|
OmapSampleMask = false;
|
||||||
OmapDepth = false;
|
OmapDepth = false;
|
||||||
|
@ -39,6 +42,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
OutputTopology = header.OutputTopology;
|
OutputTopology = header.OutputTopology;
|
||||||
MaxOutputVertices = header.MaxOutputVertexCount;
|
MaxOutputVertices = header.MaxOutputVertexCount;
|
||||||
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
||||||
|
ImapTypes = header.ImapTypes;
|
||||||
OmapTargets = header.OmapTargets;
|
OmapTargets = header.OmapTargets;
|
||||||
OmapSampleMask = header.OmapSampleMask;
|
OmapSampleMask = header.OmapSampleMask;
|
||||||
OmapDepth = header.OmapDepth;
|
OmapDepth = header.OmapDepth;
|
||||||
|
|
|
@ -4,7 +4,39 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
struct OutputMapTarget
|
enum PixelImap
|
||||||
|
{
|
||||||
|
Unused = 0,
|
||||||
|
Constant = 1,
|
||||||
|
Perspective = 2,
|
||||||
|
ScreenLinear = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImapPixelType
|
||||||
|
{
|
||||||
|
public PixelImap X { get; }
|
||||||
|
public PixelImap Y { get; }
|
||||||
|
public PixelImap Z { get; }
|
||||||
|
public PixelImap W { get; }
|
||||||
|
|
||||||
|
public ImapPixelType(PixelImap x, PixelImap y, PixelImap z, PixelImap w)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PixelImap GetFirstUsedType()
|
||||||
|
{
|
||||||
|
if (X != PixelImap.Unused) return X;
|
||||||
|
if (Y != PixelImap.Unused) return Y;
|
||||||
|
if (Z != PixelImap.Unused) return Z;
|
||||||
|
return W;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OmapTarget
|
||||||
{
|
{
|
||||||
public bool Red { get; }
|
public bool Red { get; }
|
||||||
public bool Green { get; }
|
public bool Green { get; }
|
||||||
|
@ -13,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public bool Enabled => Red || Green || Blue || Alpha;
|
public bool Enabled => Red || Green || Blue || Alpha;
|
||||||
|
|
||||||
public OutputMapTarget(bool red, bool green, bool blue, bool alpha)
|
public OmapTarget(bool red, bool green, bool blue, bool alpha)
|
||||||
{
|
{
|
||||||
Red = red;
|
Red = red;
|
||||||
Green = green;
|
Green = green;
|
||||||
|
@ -72,9 +104,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public int StoreReqStart { get; }
|
public int StoreReqStart { get; }
|
||||||
public int StoreReqEnd { get; }
|
public int StoreReqEnd { get; }
|
||||||
|
|
||||||
public OutputMapTarget[] OmapTargets { get; }
|
public ImapPixelType[] ImapTypes { get; }
|
||||||
public bool OmapSampleMask { get; }
|
|
||||||
public bool OmapDepth { get; }
|
public OmapTarget[] OmapTargets { get; }
|
||||||
|
public bool OmapSampleMask { get; }
|
||||||
|
public bool OmapDepth { get; }
|
||||||
|
|
||||||
public ShaderHeader(ReadOnlySpan<byte> code)
|
public ShaderHeader(ReadOnlySpan<byte> code)
|
||||||
{
|
{
|
||||||
|
@ -127,14 +161,30 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
StoreReqStart = commonWord4.Extract(12, 8);
|
StoreReqStart = commonWord4.Extract(12, 8);
|
||||||
StoreReqEnd = commonWord4.Extract(24, 8);
|
StoreReqEnd = commonWord4.Extract(24, 8);
|
||||||
|
|
||||||
|
ImapTypes = new ImapPixelType[32];
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
byte imap = (byte)(header[6 + i] >> (j * 8));
|
||||||
|
|
||||||
|
ImapTypes[i * 4 + j] = new ImapPixelType(
|
||||||
|
(PixelImap)((imap >> 0) & 3),
|
||||||
|
(PixelImap)((imap >> 2) & 3),
|
||||||
|
(PixelImap)((imap >> 4) & 3),
|
||||||
|
(PixelImap)((imap >> 6) & 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int type2OmapTarget = header[18];
|
int type2OmapTarget = header[18];
|
||||||
int type2Omap = header[19];
|
int type2Omap = header[19];
|
||||||
|
|
||||||
OmapTargets = new OutputMapTarget[8];
|
OmapTargets = new OmapTarget[8];
|
||||||
|
|
||||||
for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4)
|
for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4)
|
||||||
{
|
{
|
||||||
OmapTargets[offset >> 2] = new OutputMapTarget(
|
OmapTargets[offset >> 2] = new OmapTarget(
|
||||||
type2OmapTarget.Extract(offset + 0),
|
type2OmapTarget.Extract(offset + 0),
|
||||||
type2OmapTarget.Extract(offset + 1),
|
type2OmapTarget.Extract(offset + 1),
|
||||||
type2OmapTarget.Extract(offset + 2),
|
type2OmapTarget.Extract(offset + 2),
|
||||||
|
|
Reference in a new issue