Refactor attribute handling on the shader generator (#4565)

* Refactor attribute handling on the shader generator

* Implement gl_ViewportMask[]

* Add back the Intel FrontFacing bug workaround

* Fix GLSL transform feedback outputs mistmatch with fragment stage

* Shader cache version bump

* Fix geometry shader recognition

* PR feedback

* Delete GetOperandDef and GetOperandUse

* Remove replacements that are no longer needed on GLSL compilation on Vulkan

* Fix incorrect load for per-patch outputs

* Fix build
This commit is contained in:
gdkchan 2023-04-25 19:51:07 -03:00 committed by GitHub
parent 097562bc6c
commit 9f12e50a54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 1967 additions and 1746 deletions

View file

@ -2,104 +2,35 @@ namespace Ryujinx.Graphics.Shader.Translation
{
static class AttributeConsts
{
public const int TessLevelOuter0 = 0x000;
public const int TessLevelOuter1 = 0x004;
public const int TessLevelOuter2 = 0x008;
public const int TessLevelOuter3 = 0x00c;
public const int TessLevelInner0 = 0x010;
public const int TessLevelInner1 = 0x014;
public const int PrimitiveId = 0x060;
public const int Layer = 0x064;
public const int ViewportIndex = 0x068;
public const int PointSize = 0x06c;
public const int PositionX = 0x070;
public const int PositionY = 0x074;
public const int PositionZ = 0x078;
public const int PositionW = 0x07c;
public const int FrontColorDiffuseR = 0x280;
public const int FrontColorDiffuseG = 0x284;
public const int FrontColorDiffuseB = 0x288;
public const int FrontColorDiffuseA = 0x28c;
public const int FrontColorSpecularR = 0x290;
public const int FrontColorSpecularG = 0x294;
public const int FrontColorSpecularB = 0x298;
public const int FrontColorSpecularA = 0x29c;
public const int BackColorDiffuseR = 0x2a0;
public const int BackColorDiffuseG = 0x2a4;
public const int BackColorDiffuseB = 0x2a8;
public const int BackColorDiffuseA = 0x2ac;
public const int BackColorSpecularR = 0x2b0;
public const int BackColorSpecularG = 0x2b4;
public const int BackColorSpecularB = 0x2b8;
public const int BackColorSpecularA = 0x2bc;
public const int ClipDistance0 = 0x2c0;
public const int ClipDistance1 = 0x2c4;
public const int ClipDistance2 = 0x2c8;
public const int ClipDistance3 = 0x2cc;
public const int ClipDistance4 = 0x2d0;
public const int ClipDistance5 = 0x2d4;
public const int ClipDistance6 = 0x2d8;
public const int ClipDistance7 = 0x2dc;
public const int PointCoordX = 0x2e0;
public const int PointCoordY = 0x2e4;
public const int TessCoordX = 0x2f0;
public const int TessCoordY = 0x2f4;
public const int InstanceId = 0x2f8;
public const int VertexId = 0x2fc;
public const int TexCoordCount = 10;
public const int TexCoordBase = 0x300;
public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
public const int FrontFacing = 0x3fc;
public const int PrimitiveId = 0x060;
public const int Layer = 0x064;
public const int PositionX = 0x070;
public const int PositionY = 0x074;
public const int FrontColorDiffuseR = 0x280;
public const int BackColorDiffuseR = 0x2a0;
public const int ClipDistance0 = 0x2c0;
public const int ClipDistance1 = 0x2c4;
public const int ClipDistance2 = 0x2c8;
public const int ClipDistance3 = 0x2cc;
public const int ClipDistance4 = 0x2d0;
public const int ClipDistance5 = 0x2d4;
public const int ClipDistance6 = 0x2d8;
public const int ClipDistance7 = 0x2dc;
public const int FogCoord = 0x2e8;
public const int TessCoordX = 0x2f0;
public const int TessCoordY = 0x2f4;
public const int InstanceId = 0x2f8;
public const int VertexId = 0x2fc;
public const int TexCoordCount = 10;
public const int TexCoordBase = 0x300;
public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
public const int FrontFacing = 0x3fc;
public const int UserAttributesCount = 32;
public const int UserAttributeBase = 0x80;
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
public const int UserAttributeBase = 0x80;
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
public const int UserAttributePerPatchBase = 0x18;
public const int UserAttributePerPatchEnd = 0x200;
public const int LoadOutputMask = 1 << 30;
public const int Mask = 0x3fffffff;
// Note: Those attributes are used internally by the translator
// only, they don't exist on Maxwell.
public const int SpecialMask = 0xf << 24;
public const int FragmentOutputDepth = 0x1000000;
public const int FragmentOutputColorBase = 0x1000010;
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
public const int FragmentOutputIsBgraBase = 0x1000100;
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
public const int SupportBlockViewInverseX = 0x1000200;
public const int SupportBlockViewInverseY = 0x1000204;
public const int ThreadIdX = 0x2000000;
public const int ThreadIdY = 0x2000004;
public const int ThreadIdZ = 0x2000008;
public const int CtaIdX = 0x2000010;
public const int CtaIdY = 0x2000014;
public const int CtaIdZ = 0x2000018;
public const int LaneId = 0x2000020;
public const int InvocationId = 0x2000024;
public const int PatchVerticesIn = 0x2000028;
public const int EqMask = 0x2000030;
public const int GeMask = 0x2000034;
public const int GtMask = 0x2000038;
public const int LeMask = 0x200003c;
public const int LtMask = 0x2000040;
public const int ThreadKill = 0x2000044;
public const int BaseInstance = 0x2000050;
public const int BaseVertex = 0x2000054;
public const int InstanceIndex = 0x2000058;
public const int VertexIndex = 0x200005c;
public const int DrawIndex = 0x2000060;
public const int UserAttributePerPatchEnd = 0x200;
}
}

View file

@ -1,210 +0,0 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.Translation
{
readonly struct AttributeInfo
{
private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
{
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
// Special.
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
{ AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
{ AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
{ AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
};
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
{
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
};
public int BaseValue { get; }
public int Value { get; }
public int Length { get; }
public AggregateType Type { get; }
public bool IsBuiltin { get; }
public bool IsValid => Type != AggregateType.Invalid;
public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true)
{
BaseValue = baseValue;
Value = baseValue + index * 4;
Length = length;
Type = type;
IsBuiltin = isBuiltin;
}
public int GetInnermostIndex()
{
return (Value - BaseValue) / 4;
}
public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
{
return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
}
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
{
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
{
return false;
}
return From(config, value, isOutAttr).IsValid;
}
public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
{
return FromPatch(config, value, isOutAttr).IsValid;
}
public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
{
value &= ~3;
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
{
int location = (value - AttributeConsts.UserAttributeBase) / 16;
AggregateType elemType;
if (config.Stage == ShaderStage.Vertex && !isOutAttr)
{
elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType();
}
else
{
elemType = AggregateType.FP32;
}
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
}
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
{
int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
{
AttributeType.Sint => AggregateType.S32,
AttributeType.Uint => AggregateType.U32,
_ => AggregateType.FP32
};
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
}
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
{
return new AttributeInfo(value, 0, 1, AggregateType.FP32);
}
else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
{
return info;
}
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
}
public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
{
value &= ~3;
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
{
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
}
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
{
return info;
}
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
}
public static bool IsArrayBuiltIn(int attr)
{
if (attr <= AttributeConsts.TessLevelInner1 ||
attr == AttributeConsts.TessCoordX ||
attr == AttributeConsts.TessCoordY)
{
return false;
}
return (attr & AttributeConsts.SpecialMask) == 0;
}
public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
{
if (isOutAttr)
{
return stage == ShaderStage.TessellationControl;
}
else
{
return stage == ShaderStage.TessellationControl ||
stage == ShaderStage.TessellationEvaluation ||
stage == ShaderStage.Geometry;
}
}
public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr)
{
if (isOutAttr)
{
return false;
}
else
{
return stage == ShaderStage.TessellationControl ||
stage == ShaderStage.TessellationEvaluation ||
stage == ShaderStage.Geometry;
}
}
}
}

View file

@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Shader.Translation
(Config.Options.Flags & TranslationFlags.VertexA) == 0)
{
// Vulkan requires the point size to be always written on the shader if the primitive topology is points.
this.Copy(Attribute(AttributeConsts.PointSize), ConstF(Config.GpuAccessor.QueryPointSize()));
this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(Config.GpuAccessor.QueryPointSize()));
}
}
@ -87,6 +87,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return dest;
}
public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources)
{
Operation operation = new Operation(inst, storageKind, dest, sources);
_operations.Add(operation);
return dest;
}
public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources)
{
Operand[] dests = new[] { dest.Item1, dest.Item2 };
@ -223,30 +232,35 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask);
Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask);
Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX);
Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY);
Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0));
Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1));
Operand negativeOne = ConstF(-1.0f);
this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
}
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
{
Operand z = Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask);
Operand w = Attribute(AttributeConsts.PositionW | AttributeConsts.LoadOutputMask);
Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
Operand halfW = this.FPMultiply(w, ConstF(0.5f));
this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
}
if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute)
{
Config.SetUsedFeature(FeatureFlags.RtLayer);
this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask));
int attrVecIndex = Config.GpLayerInputAttribute >> 2;
int attrComponentIndex = Config.GpLayerInputAttribute & 3;
Operand layer = this.Load(StorageKind.Output, IoVariable.UserDefined, null, Const(attrVecIndex), Const(attrComponentIndex));
this.Store(StorageKind.Output, IoVariable.Layer, null, layer);
}
}
@ -255,9 +269,9 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
oldXLocal = Local();
this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)));
oldYLocal = Local();
this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)));
}
else
{
@ -268,7 +282,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
{
oldZLocal = Local();
this.Copy(oldZLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask));
this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
}
else
{
@ -293,17 +307,30 @@ namespace Ryujinx.Graphics.Shader.Translation
}
else if (Config.Stage == ShaderStage.Geometry)
{
void WriteOutput(int index, int primIndex)
void WritePositionOutput(int primIndex)
{
Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex));
Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex));
Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex));
Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex));
Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0));
Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1));
Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2));
Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3));
this.Copy(Attribute(index), x);
this.Copy(Attribute(index + 4), y);
this.Copy(Attribute(index + 8), z);
this.Copy(Attribute(index + 12), w);
this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x);
this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y);
this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z);
this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w);
}
void WriteUserDefinedOutput(int index, int primIndex)
{
Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(0));
Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(1));
Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(2));
Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(3));
this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x);
this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y);
this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z);
this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w);
}
if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
@ -312,13 +339,13 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int primIndex = 0; primIndex < inputVertices; primIndex++)
{
WriteOutput(AttributeConsts.PositionX, primIndex);
WritePositionOutput(primIndex);
int passthroughAttributes = Config.PassthroughAttributes;
while (passthroughAttributes != 0)
{
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
WriteUserDefinedOutput(index, primIndex);
Config.SetOutputUserAttribute(index);
passthroughAttributes &= ~(1 << index);
}
@ -337,11 +364,9 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.OmapDepth)
{
Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr);
this.Copy(dest, src);
this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src);
}
AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
@ -390,32 +415,30 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16;
Operand src = Register(regIndexBase + component, RegisterType.Gpr);
// Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
if (!supportsBgra && (component == 0 || component == 2))
{
Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4);
Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex));
Operand lblIsBgra = Label();
Operand lblEnd = Label();
this.BranchIfTrue(lblIsBgra, isBgra);
this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
this.Branch(lblEnd);
MarkLabel(lblIsBgra);
this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src);
this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src);
MarkLabel(lblEnd);
}
else
{
this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
}
}
@ -441,8 +464,11 @@ namespace Ryujinx.Graphics.Shader.Translation
// 11 01 01 01 01 00 00 00
Operand ditherMask = Const(unchecked((int)0xfbb99110u));
Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1));
Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1));
Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1));
Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1));
Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
Operand alpha = Register(3, RegisterType.Gpr);

View file

@ -7,54 +7,54 @@ namespace Ryujinx.Graphics.Shader.Translation
{
static class EmitterContextInsts
{
public static Operand AtomicAdd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicAdd | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicAdd, storageKind, Local(), a, b, c);
}
public static Operand AtomicAnd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicAnd | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicAnd, storageKind, Local(), a, b, c);
}
public static Operand AtomicCompareAndSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c, Operand d)
public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c, Operand d)
{
return context.Add(Instruction.AtomicCompareAndSwap | mr, Local(), a, b, c, d);
return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), a, b, c, d);
}
public static Operand AtomicMaxS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicMaxS32 | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), a, b, c);
}
public static Operand AtomicMaxU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicMaxU32 | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), a, b, c);
}
public static Operand AtomicMinS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicMinS32 | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicMinS32, storageKind, Local(), a, b, c);
}
public static Operand AtomicMinU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicMinU32 | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicMinU32, storageKind, Local(), a, b, c);
}
public static Operand AtomicOr(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicOr | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicOr, storageKind, Local(), a, b, c);
}
public static Operand AtomicSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicSwap | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicSwap, storageKind, Local(), a, b, c);
}
public static Operand AtomicXor(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.AtomicXor | mr, Local(), a, b, c);
return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c);
}
public static Operand Ballot(this EmitterContext context, Operand a)
@ -549,9 +549,36 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(fpType | Instruction.IsNan, Local(), a);
}
public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null)
{
return context.Add(Instruction.LoadAttribute, Local(), a, b, c);
return primVertex != null
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex)
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable));
}
public static Operand Load(
this EmitterContext context,
StorageKind storageKind,
IoVariable ioVariable,
Operand primVertex,
Operand elemIndex)
{
return primVertex != null
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex)
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex);
}
public static Operand Load(
this EmitterContext context,
StorageKind storageKind,
IoVariable ioVariable,
Operand primVertex,
Operand arrayIndex,
Operand elemIndex)
{
return primVertex != null
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex)
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex);
}
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
@ -662,9 +689,43 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
}
public static Operand StoreAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
public static Operand Store(
this EmitterContext context,
StorageKind storageKind,
IoVariable ioVariable,
Operand invocationId,
Operand value)
{
return context.Add(Instruction.StoreAttribute, null, a, b, c);
return invocationId != null
? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, value)
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), value);
}
public static Operand Store(
this EmitterContext context,
StorageKind storageKind,
IoVariable ioVariable,
Operand invocationId,
Operand elemIndex,
Operand value)
{
return invocationId != null
? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, elemIndex, value)
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), elemIndex, value);
}
public static Operand Store(
this EmitterContext context,
StorageKind storageKind,
IoVariable ioVariable,
Operand invocationId,
Operand arrayIndex,
Operand elemIndex,
Operand value)
{
return invocationId != null
? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, arrayIndex, elemIndex, value)
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
}
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)

View file

@ -16,20 +16,15 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
public const int UbeFirstCbuf = 8;
public static bool UsesGlobalMemory(Instruction inst)
public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind)
{
return (inst.IsAtomic() && IsGlobalMr(inst)) ||
return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) ||
inst == Instruction.LoadGlobal ||
inst == Instruction.StoreGlobal ||
inst == Instruction.StoreGlobal16 ||
inst == Instruction.StoreGlobal8;
}
private static bool IsGlobalMr(Instruction inst)
{
return (inst & Instruction.MrMask) == Instruction.MrGlobal;
}
public static int GetStorageCbOffset(ShaderStage stage, int slot)
{
return GetStorageBaseCbOffset(stage) + slot * StorageDescSize;

View file

@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
if (UsesGlobalMemory(operation.Inst))
if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
Operand source = operation.GetSource(0);
@ -104,9 +104,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (isAtomic)
{
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
storageOp = new Operation(inst, operation.Dest, sources);
storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
}
else if (operation.Inst == Instruction.LoadGlobal)
{

View file

@ -170,10 +170,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return false;
}
return x.Type == OperandType.Attribute ||
x.Type == OperandType.AttributePerPatch ||
x.Type == OperandType.Constant ||
x.Type == OperandType.ConstantBuffer;
// TODO: Handle Load operations with the same storage and the same constant parameters.
return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
}
private static bool PropagatePack(Operation packOp)

View file

@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (hasConstantBufferDrawParameters)
{
if (ReplaceConstantBufferWithDrawParameters(operation))
if (ReplaceConstantBufferWithDrawParameters(node, operation))
{
config.SetUsedFeature(FeatureFlags.DrawParameters);
}
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation
nextNode = node.Next;
}
else if (UsesGlobalMemory(operation.Inst))
else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
}
@ -169,9 +169,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (isAtomic)
{
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
storageOp = new Operation(inst, operation.Dest, sources);
storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
}
else if (operation.Inst == Instruction.LoadGlobal)
{
@ -708,8 +706,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return node;
}
private static bool ReplaceConstantBufferWithDrawParameters(Operation operation)
private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
{
Operand GenerateLoad(IoVariable ioVariable)
{
Operand value = Local();
node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, value, Const((int)ioVariable)));
return value;
}
bool modified = false;
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
@ -721,15 +726,15 @@ namespace Ryujinx.Graphics.Shader.Translation
switch (src.GetCbufOffset())
{
case Constants.NvnBaseVertexByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex));
operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseVertex));
modified = true;
break;
case Constants.NvnBaseInstanceByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance));
operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseInstance));
modified = true;
break;
case Constants.NvnDrawIndexByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex));
operation.SetSource(srcIndex, GenerateLoad(IoVariable.DrawIndex));
modified = true;
break;
}

View file

@ -41,6 +41,46 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool TransformFeedbackEnabled { get; }
private TransformFeedbackOutput[] _transformFeedbackOutputs;
readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable>
{
public IoVariable IoVariable { get; }
public int Location { get; }
public int Component { get; }
public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0)
{
IoVariable = ioVariable;
Location = location;
Component = component;
}
public override bool Equals(object other)
{
return other is TransformFeedbackVariable tfbVar && Equals(tfbVar);
}
public bool Equals(TransformFeedbackVariable other)
{
return IoVariable == other.IoVariable &&
Location == other.Location &&
Component == other.Component;
}
public override int GetHashCode()
{
return (int)IoVariable | (Location << 8) | (Component << 16);
}
public override string ToString()
{
return $"{IoVariable}.{Location}.{Component}";
}
}
private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions;
public int Size { get; private set; }
public byte ClipDistancesWritten { get; private set; }
@ -102,6 +142,8 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor = gpuAccessor;
Options = options;
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
@ -147,6 +189,173 @@ namespace Ryujinx.Graphics.Shader.Translation
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
}
private void EnsureTransformFeedbackInitialized()
{
if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null)
{
TransformFeedbackOutput[] transformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
ulong vecMap = 0UL;
for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
{
var locations = GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
var stride = GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
for (int i = 0; i < locations.Length; i++)
{
byte wordOffset = locations[i];
if (wordOffset < 0xc0)
{
transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
vecMap |= 1UL << (wordOffset / 4);
}
}
}
_transformFeedbackOutputs = transformFeedbackOutputs;
while (vecMap != 0)
{
int vecIndex = BitOperations.TrailingZeroCount(vecMap);
for (int subIndex = 0; subIndex < 4; subIndex++)
{
int wordOffset = vecIndex * 4 + subIndex;
int byteOffset = wordOffset * 4;
if (transformFeedbackOutputs[wordOffset].Valid)
{
IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location);
int component = 0;
if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true))
{
component = subIndex;
}
var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
_transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]);
}
}
vecMap &= ~(1UL << vecIndex);
}
}
}
public TransformFeedbackOutput[] GetTransformFeedbackOutputs()
{
EnsureTransformFeedbackInitialized();
return _transformFeedbackOutputs;
}
public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput)
{
EnsureTransformFeedbackInitialized();
var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput);
}
private bool HasTransformFeedbackOutputs()
{
return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment);
}
public bool HasTransformFeedbackOutputs(bool isOutput)
{
return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment));
}
public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput)
{
if (ioVariable == IoVariable.UserDefined)
{
return (!isOutput && !UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
(isOutput && !UsedFeatures.HasFlag(FeatureFlags.OaIndexing));
}
return ioVariable == IoVariable.FragmentOutputColor;
}
public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput)
{
if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput))
{
return false;
}
return GetTransformFeedbackOutputComponents(location, component) == 1;
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset)
{
EnsureTransformFeedbackInitialized();
return _transformFeedbackOutputs[wordOffset];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component);
}
public int GetTransformFeedbackOutputComponents(int location, int component)
{
EnsureTransformFeedbackInitialized();
int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4;
int index = baseIndex + component;
int count = 1;
for (; count < 4; count++)
{
ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1];
ref var curr = ref _transformFeedbackOutputs[baseIndex + count];
int prevOffset = prev.Offset;
int currOffset = curr.Offset;
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
{
break;
}
}
if (baseIndex + count <= index)
{
return 1;
}
return count;
}
public AggregateType GetFragmentOutputColorType(int location)
{
return AggregateType.Vector4 | GpuAccessor.QueryFragmentOutputType(location).ToAggregateType();
}
public AggregateType GetUserDefinedType(int location, bool isOutput)
{
if ((!isOutput && UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
(isOutput && UsedFeatures.HasFlag(FeatureFlags.OaIndexing)))
{
return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32;
}
AggregateType type = AggregateType.Vector4;
if (Stage == ShaderStage.Vertex && !isOutput)
{
type |= GpuAccessor.QueryAttributeType(location).ToAggregateType();
}
else
{
type |= AggregateType.FP32;
}
return type;
}
public int GetDepthRegister()
{
// The depth register is always two registers after the last color output.
@ -184,7 +393,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return format;
}
private bool FormatSupportsAtomic(TextureFormat format)
private static bool FormatSupportsAtomic(TextureFormat format)
{
return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint;
}

View file

@ -53,40 +53,80 @@ namespace Ryujinx.Graphics.Shader.Translation
return false;
}
if (operation.Inst == Instruction.StoreAttribute)
if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output)
{
return false;
}
Operand src = operation.GetSource(operation.SourcesCount - 1);
Operation srcAttributeAsgOp = null;
if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute)
{
Operand src = operation.GetSource(0);
if (src.Type == OperandType.LocalVariable && src.AsgOp is Operation asgOp && asgOp.Inst == Instruction.LoadAttribute)
if (src.Type == OperandType.LocalVariable &&
src.AsgOp is Operation asgOp &&
asgOp.Inst == Instruction.Load &&
asgOp.StorageKind.IsInputOrOutput())
{
src = Attribute(asgOp.GetSource(0).Value);
if (asgOp.StorageKind != StorageKind.Input)
{
return false;
}
srcAttributeAsgOp = asgOp;
}
if (src.Type == OperandType.Attribute)
if (srcAttributeAsgOp != null)
{
if (operation.Dest.Value == AttributeConsts.Layer)
IoVariable dstAttribute = (IoVariable)operation.GetSource(0).Value;
IoVariable srcAttribute = (IoVariable)srcAttributeAsgOp.GetSource(0).Value;
if (dstAttribute == IoVariable.Layer && srcAttribute == IoVariable.UserDefined)
{
if ((src.Value & AttributeConsts.LoadOutputMask) != 0)
if (srcAttributeAsgOp.SourcesCount != 4)
{
return false;
}
writesLayer = true;
layerInputAttr = src.Value;
layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value;;
}
else if (src.Value != operation.Dest.Value)
else
{
return false;
if (dstAttribute != srcAttribute)
{
return false;
}
int inputsCount = operation.SourcesCount - 2;
if (dstAttribute == IoVariable.UserDefined)
{
if (operation.GetSource(1).Value != srcAttributeAsgOp.GetSource(1).Value)
{
return false;
}
inputsCount--;
}
for (int i = 0; i < inputsCount; i++)
{
int dstIndex = operation.SourcesCount - 2 - i;
int srcIndex = srcAttributeAsgOp.SourcesCount - 1 - i;
if ((dstIndex | srcIndex) < 0)
{
return false;
}
if (operation.GetSource(dstIndex).Type != OperandType.Constant ||
srcAttributeAsgOp.GetSource(srcIndex).Type != OperandType.Constant ||
operation.GetSource(dstIndex).Value != srcAttributeAsgOp.GetSource(srcIndex).Value)
{
return false;
}
}
}
}
else if (src.Type == OperandType.Constant)
{
int dstComponent = (operation.Dest.Value >> 2) & 3;
int dstComponent = operation.GetSource(operation.SourcesCount - 2).Value;
float expectedValue = dstComponent == 3 ? 1f : 0f;
if (src.AsFloat() != expectedValue)

View file

@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (config.Stage == ShaderStage.Vertex)
{
InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
InitializePositionOutput(context);
}
UInt128 usedAttributes = context.Config.NextInputAttributesComponents;
@ -194,20 +194,23 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
InitializeOutputComponent(context, vecIndex, index & 3, perPatch: false);
}
if (context.Config.NextUsedInputAttributesPerPatch != null)
{
foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.Order())
{
InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true);
InitializeOutput(context, vecIndex, perPatch: true);
}
}
if (config.NextUsesFixedFuncAttributes)
{
for (int i = 0; i < 4 + AttributeConsts.TexCoordCount; i++)
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1;
for (int i = fixedStartAttr; i < fixedStartAttr + 5 + AttributeConsts.TexCoordCount; i++)
{
int index = config.GetFreeUserAttribute(isOutput: true, i);
if (index < 0)
@ -215,26 +218,58 @@ namespace Ryujinx.Graphics.Shader.Translation
break;
}
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
InitializeOutput(context, index, perPatch: false);
config.SetOutputUserAttributeFixedFunc(index);
}
}
}
private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch)
private static void InitializePositionOutput(EmitterContext context)
{
for (int c = 0; c < 4; c++)
{
int attrOffset = baseAttr + c * 4;
InitializeOutputComponent(context, attrOffset, perPatch);
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
}
}
private static void InitializeOutputComponent(EmitterContext context, int attrOffset, bool perPatch)
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
{
int c = (attrOffset >> 2) & 3;
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
for (int c = 0; c < 4; c++)
{
InitializeOutputComponent(context, location, c, perPatch);
}
}
private static void InitializeOutputComponent(EmitterContext context, int location, int c, bool perPatch)
{
StorageKind storageKind = perPatch ? StorageKind.OutputPerPatch : StorageKind.Output;
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
{
Operand invocationId = null;
if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch)
{
invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
}
int index = location * 4 + c;
context.Store(storageKind, IoVariable.UserDefined, invocationId, Const(index), ConstF(c == 3 ? 1f : 0f));
}
else
{
if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch)
{
Operand invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
context.Store(storageKind, IoVariable.UserDefined, Const(location), invocationId, Const(c), ConstF(c == 3 ? 1f : 0f));
}
else
{
context.Store(storageKind, IoVariable.UserDefined, null, Const(location), Const(c), ConstF(c == 3 ? 1f : 0f));
}
}
}
private static void EmitOps(EmitterContext context, Block block)

View file

@ -34,15 +34,16 @@ namespace Ryujinx.Graphics.Shader.Translation
_config = config;
}
private static bool IsUserAttribute(Operand operand)
private static bool IsLoadUserDefined(Operation operation)
{
if (operand != null && operand.Type.IsAttribute())
{
int value = operand.Value & AttributeConsts.Mask;
return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd;
}
// TODO: Check if sources count match and all sources are constant.
return operation.Inst == Instruction.Load && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined;
}
return false;
private static bool IsStoreUserDefined(Operation operation)
{
// TODO: Check if sources count match and all sources are constant.
return operation.Inst == Instruction.Store && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined;
}
private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart)
@ -68,9 +69,9 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operation operation = a[0].Code[index];
if (IsUserAttribute(operation.Dest))
if (IsStoreUserDefined(operation))
{
int tIndex = (operation.Dest.Value - AttributeConsts.UserAttributeBase) / 4;
int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value;
Operand temp = temps[tIndex];
@ -82,6 +83,7 @@ namespace Ryujinx.Graphics.Shader.Translation
}
operation.Dest = temp;
operation.TurnIntoCopy(operation.GetSource(operation.SourcesCount - 1));
}
if (operation.Inst == Instruction.Return)
@ -100,18 +102,15 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operation operation = b[0].Code[index];
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
if (IsLoadUserDefined(operation))
{
Operand src = operation.GetSource(srcIndex);
int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value;
if (IsUserAttribute(src))
Operand temp = temps[tIndex];
if (temp != null)
{
Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4];
if (temp != null)
{
operation.SetSource(srcIndex, temp);
}
operation.TurnIntoCopy(temp);
}
}
@ -209,15 +208,15 @@ namespace Ryujinx.Graphics.Shader.Translation
{
int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4;
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
Operand value = context.Load(StorageKind.Input, IoVariable.UserDefined, Const(v), Const(attrIndex), Const(c));
if (attr == layerOutputAttr)
{
context.Copy(Attribute(AttributeConsts.Layer), value);
context.Store(StorageKind.Output, IoVariable.Layer, null, value);
}
else
{
context.Copy(Attribute(attr), value);
context.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(attrIndex), Const(c), value);
config.SetOutputUserAttribute(attrIndex);
}
@ -227,11 +226,9 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int c = 0; c < 4; c++)
{
int attr = AttributeConsts.PositionX + c * 4;
Operand value = context.Load(StorageKind.Input, IoVariable.Position, Const(v), Const(c));
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
context.Copy(Attribute(attr), value);
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), value);
}
context.EmitVertex();