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:
parent
097562bc6c
commit
9f12e50a54
56 changed files with 1967 additions and 1746 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue