Support shader gl_Color, gl_SecondaryColor and gl_TexCoord built-ins (#2817)

* Support shader gl_Color, gl_SecondaryColor and gl_TexCoord built-ins

* Shader cache version bump

* Fix back color value on fragment shader

* Disable IPA multiplication for fixed function attributes and back color selection
This commit is contained in:
gdkchan 2021-11-08 13:18:46 -03:00 committed by GitHub
parent 3dee712164
commit 911ea38e93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 220 additions and 38 deletions

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Version of the codegen (to be changed when codegen or guest format change).
/// </summary>
private const ulong ShaderCodeGenVersion = 2826;
private const ulong ShaderCodeGenVersion = 2816;
// Progress reporting helpers
private volatile int _shaderCount;

View file

@ -319,6 +319,13 @@ namespace Ryujinx.Graphics.Shader.Decoders
config.SetInputUserAttribute(index, perPatch);
}
}
if (!isStore &&
((attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) ||
(attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)))
{
config.SetUsedFeature(FeatureFlags.FixedFuncAttr);
}
}
}
}

View file

@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
{
int offset = op.Imm11 + index * 4;
int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O);
context.FlagAttributeRead(offset);
@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else
{
int offset = op.Imm11 + index * 4;
int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O);
context.FlagAttributeRead(offset);
@ -101,6 +101,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
int offset = op.Imm11 + index * 4;
if (!context.Config.IsUsedOutputAttribute(offset))
{
return;
}
offset = FixedFuncToUserAttribute(context.Config, offset, isOutput: true);
context.FlagAttributeWritten(offset);
Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset);
@ -118,6 +125,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand res;
bool isFixedFunc = false;
if (op.Idx)
{
Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
@ -130,7 +139,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else
{
res = Attribute(op.Imm10);
isFixedFunc = TryFixedFuncToUserAttributeIpa(context, op.Imm10, out res);
if (op.Imm10 >= AttributeConsts.UserAttributeBase && op.Imm10 < AttributeConsts.UserAttributeEnd)
{
@ -143,7 +152,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
if (op.IpaOp == IpaOp.Multiply)
if (op.IpaOp == IpaOp.Multiply && !isFixedFunc)
{
Operand srcB = GetSrcReg(context, op.SrcB);
@ -204,5 +213,72 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.EndPrimitive();
}
}
private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr)
{
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR)
{
// TODO: If two sided rendering is enabled, then this should return
// FrontColor if the fragment is front facing, and back color otherwise.
int index = (attr - AttributeConsts.FrontColorDiffuseR) >> 4;
int userAttrIndex = context.Config.GetFreeUserAttribute(isOutput: false, index);
Operand frontAttr = Attribute(AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf));
context.Config.SetInputUserAttributeFixedFunc(userAttrIndex);
selectedAttr = frontAttr;
return true;
}
else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0)
{
selectedAttr = ConstF(((attr >> 2) & 3) == 3 ? 1f : 0f);
return true;
}
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
{
selectedAttr = Attribute(FixedFuncToUserAttribute(context.Config, attr, AttributeConsts.TexCoordBase, 4, isOutput: false));
return true;
}
selectedAttr = Attribute(attr);
return false;
}
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
{
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
{
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, 0, isOutput);
}
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
{
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, 4, isOutput);
}
return attr;
}
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput)
{
int index = (attr - baseAttr) >> 4;
int userAttrIndex = config.GetFreeUserAttribute(isOutput, index);
if ((uint)userAttrIndex < Constants.MaxAttributes)
{
userAttrIndex += baseIndex;
attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf);
if (isOutput)
{
config.SetOutputUserAttributeFixedFunc(userAttrIndex);
}
else
{
config.SetInputUserAttributeFixedFunc(userAttrIndex);
}
}
return attr;
}
}
}

View file

@ -2,33 +2,52 @@ 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 Layer = 0x064;
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 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 FrontFacing = 0x3fc;
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 Layer = 0x064;
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 UserAttributesCount = 32;
public const int UserAttributeBase = 0x80;

View file

@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Shader.Translation
RtLayer = 1 << 4,
CbIndexing = 1 << 5,
IaIndexing = 1 << 6,
OaIndexing = 1 << 7
OaIndexing = 1 << 7,
FixedFuncAttr = 1 << 8
}
}

View file

@ -43,11 +43,14 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly TranslationCounts _counts;
public bool NextUsesFixedFuncAttributes { get; private set; }
public int UsedInputAttributes { get; private set; }
public int UsedInputAttributesPerPatch { get; private set; }
public int UsedOutputAttributes { get; private set; }
public int UsedInputAttributesPerPatch { get; private set; }
public int UsedOutputAttributesPerPatch { get; private set; }
public int PassthroughAttributes { get; private set; }
private int _nextUsedInputAttributes;
private int _thisUsedInputAttributes;
private int _usedConstantBuffers;
private int _usedStorageBuffers;
@ -224,6 +227,16 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
public void SetInputUserAttributeFixedFunc(int index)
{
UsedInputAttributes |= 1 << index;
}
public void SetOutputUserAttributeFixedFunc(int index)
{
UsedOutputAttributes |= 1 << index;
}
public void SetInputUserAttribute(int index, bool perPatch)
{
if (perPatch)
@ -232,7 +245,10 @@ namespace Ryujinx.Graphics.Shader.Translation
}
else
{
UsedInputAttributes |= 1 << index;
int mask = 1 << index;
UsedInputAttributes |= mask;
_thisUsedInputAttributes |= mask;
}
}
@ -248,8 +264,16 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
public void MergeFromtNextStage(ShaderConfig config)
{
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
}
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
{
_nextUsedInputAttributes = mask;
if (GpPassthrough)
{
PassthroughAttributes = mask & ~UsedOutputAttributes;
@ -261,6 +285,47 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
public bool IsUsedOutputAttribute(int attr)
{
// The check for fixed function attributes on the next stage is conservative,
// returning false if the output is just not used by the next stage is also valid.
if (NextUsesFixedFuncAttributes &&
attr >= AttributeConsts.UserAttributeBase &&
attr < AttributeConsts.UserAttributeEnd)
{
int index = (attr - AttributeConsts.UserAttributeBase) >> 4;
return (_nextUsedInputAttributes & (1 << index)) != 0;
}
return true;
}
public int GetFreeUserAttribute(bool isOutput, int index)
{
int useMask = isOutput ? _nextUsedInputAttributes : _thisUsedInputAttributes;
int bit = -1;
while (useMask != -1)
{
bit = BitOperations.TrailingZeroCount(~useMask);
if (bit == 32)
{
bit = -1;
break;
}
else if (index < 1)
{
break;
}
useMask |= 1 << bit;
index--;
}
return bit;
}
public void SetAllInputUserAttributes()
{
UsedInputAttributes |= Constants.AllAttributesMask;

View file

@ -232,6 +232,22 @@ namespace Ryujinx.Graphics.Shader.Translation
usedAttributesPerPatch &= ~(1 << index);
}
if (config.NextUsesFixedFuncAttributes)
{
for (int i = 0; i < 4 + AttributeConsts.TexCoordCount; i++)
{
int index = config.GetFreeUserAttribute(isOutput: true, i);
if (index < 0)
{
break;
}
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
config.SetOutputUserAttributeFixedFunc(index);
}
}
}
private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch)

View file

@ -136,9 +136,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (nextStage != null)
{
_config.MergeOutputUserAttributes(
nextStage._config.UsedInputAttributes,
nextStage._config.UsedInputAttributesPerPatch);
_config.MergeFromtNextStage(nextStage._config);
}
FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _);