diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 4e1e130cb8..353c5dfedd 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2826; + private const ulong ShaderCodeGenVersion = 2816; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 714aaad342..80d2cb4af1 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -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); + } } } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index f82b835cab..e2131602a0 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -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; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 128013d8c8..370af00973 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -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; diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index f602ea64dd..a2363fcbb4 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -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 } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 2314016e2f..df14a5edca 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -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; diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index a658697b7f..cef25350a3 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -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) diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index b19e39af76..b4e61cb63c 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -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 _);