Fix incorrect tessellation inputs/outputs (#3728)

* Fix incorrect tessellation inputs/outputs

* Shader cache version bump
This commit is contained in:
gdkchan 2022-10-01 02:35:52 -03:00 committed by GitHub
parent dbe43c1719
commit 9c2500de5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 284 additions and 164 deletions

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3697; private const uint CodeGenVersion = 3728;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View file

@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@ -163,9 +164,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else if (context.Config.Stage == ShaderStage.TessellationEvaluation) else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
{ {
bool tessCw = context.Config.GpuAccessor.QueryTessCw();
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
// We invert the front face on Vulkan backend, so we need to do that here aswell.
tessCw = !tessCw;
}
string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl(); string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl();
string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl(); string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl();
string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw"; string windingOrder = tessCw ? "cw" : "ccw";
context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;");
context.AppendLine(); context.AppendLine();
@ -185,14 +194,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine(); context.AppendLine();
} }
if (context.Config.UsedInputAttributesPerPatch != 0) if (context.Config.UsedInputAttributesPerPatch.Count != 0)
{ {
DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch); DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch);
context.AppendLine(); context.AppendLine();
} }
if (context.Config.UsedOutputAttributesPerPatch != 0) if (context.Config.UsedOutputAttributesPerPatch.Count != 0)
{ {
DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch); DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch);
@ -509,13 +518,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
} }
private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes) private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
{ {
while (usedAttributes != 0) foreach (int attr in attrs.OrderBy(x => x))
{ {
int index = BitOperations.TrailingZeroCount(usedAttributes); DeclareInputAttributePerPatch(context, attr);
DeclareInputAttributePerPatch(context, index);
usedAttributes &= ~(1 << index);
} }
} }
@ -566,16 +573,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr) private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr)
{ {
string layout = string.Empty; int location = context.Config.GetPerPatchAttributeLocation(attr);
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
layout = $"layout (location = {32 + attr}) ";
}
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
context.AppendLine($"{layout}patch in vec4 {name};"); context.AppendLine($"layout (location = {location}) patch in vec4 {name};");
} }
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
@ -624,28 +625,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
} }
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes) private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
{ {
while (usedAttributes != 0) foreach (int attr in attrs.OrderBy(x => x))
{ {
int index = BitOperations.TrailingZeroCount(usedAttributes); DeclareOutputAttributePerPatch(context, attr);
DeclareOutputAttributePerPatch(context, index);
usedAttributes &= ~(1 << index);
} }
} }
private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr) private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr)
{ {
string layout = string.Empty; int location = context.Config.GetPerPatchAttributeLocation(attr);
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
layout = $"layout (location = {32 + attr}) ";
}
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
context.AppendLine($"{layout}patch out vec4 {name};"); context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
} }
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)

View file

@ -28,12 +28,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>() private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
{ {
{ AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) },
{ AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) },
{ AttributeConsts.TessLevelOuter2, new BuiltInAttribute("gl_TessLevelOuter[2]", VariableType.F32) },
{ AttributeConsts.TessLevelOuter3, new BuiltInAttribute("gl_TessLevelOuter[3]", VariableType.F32) },
{ AttributeConsts.TessLevelInner0, new BuiltInAttribute("gl_TessLevelInner[0]", VariableType.F32) },
{ AttributeConsts.TessLevelInner1, new BuiltInAttribute("gl_TessLevelInner[1]", VariableType.F32) },
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
@ -170,7 +164,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
value &= AttributeConsts.Mask & ~3; value &= AttributeConsts.Mask & ~3;
char swzMask = GetSwizzleMask((value >> 2) & 3); char swzMask = GetSwizzleMask((value >> 2) & 3);
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) if (perPatch)
{
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
{
value -= AttributeConsts.UserAttributePerPatchBase;
return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}";
}
else if (value < AttributeConsts.UserAttributePerPatchBase)
{
return value switch
{
AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]",
AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]",
AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]",
AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]",
AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]",
AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]",
_ => null
};
}
}
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
{ {
value -= AttributeConsts.UserAttributeBase; value -= AttributeConsts.UserAttributeBase;
@ -180,11 +196,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
if (!indexable && perPatch)
{
prefix = DefaultNames.PerPatchAttributePrefix;
}
if (indexable) if (indexable)
{ {
string name = prefix; string name = prefix;
@ -202,7 +213,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
string name = $"{prefix}{(value >> 4)}_{swzMask}"; string name = $"{prefix}{(value >> 4)}_{swzMask}";
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{ {
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
} }
@ -213,7 +224,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
string name = $"{prefix}{(value >> 4)}"; string name = $"{prefix}{(value >> 4)}";
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{ {
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
} }
@ -277,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
string name = builtInAttr.Name; string name = builtInAttr.Name;
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value))
{ {
name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}";
} }

View file

@ -382,17 +382,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType) public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
{ {
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var attrInfo = AttributeInfo.From(Config, attr, isOutAttr); var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr);
int attrOffset = attrInfo.BaseValue; int attrOffset = attrInfo.BaseValue;
Instruction ioVariable; Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
elemType = attrInfo.Type & AggregateType.ElementTypeMask; elemType = attrInfo.Type & AggregateType.ElementTypeMask;
ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
{ {
return ioVariable; return ioVariable;
@ -404,7 +400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr) public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr)
{ {
if (!AttributeInfo.Validate(Config, attr, isOutAttr: false)) if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false))
{ {
return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
} }

View file

@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
foreach (int attr in inputs) foreach (int attr in inputs)
{ {
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false)) if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
{ {
continue; continue;
} }
@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
foreach (int attr in outputs) foreach (int attr in outputs)
{ {
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true)) if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
{ {
continue; continue;
} }
@ -519,7 +519,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch) ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
: (isOutAttr ? context.Outputs : context.Inputs); : (isOutAttr ? context.Outputs : context.Inputs);
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); var attrInfo = perPatch
? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
: AttributeInfo.From(context.Config, attr, isOutAttr);
if (dict.ContainsKey(attrInfo.BaseValue)) if (dict.ContainsKey(attrInfo.BaseValue))
{ {
@ -544,11 +546,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var spvType = context.TypePointer(storageClass, attrType); var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass); var spvVar = context.Variable(spvType, storageClass);
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
if (builtInPassthrough) if (builtInPassthrough)
{ {
context.Decorate(spvVar, Decoration.PassthroughNV); context.Decorate(spvVar, Decoration.PassthroughNV);
@ -556,6 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (attrInfo.IsBuiltin) if (attrInfo.IsBuiltin)
{ {
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
@ -569,6 +571,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
} }
else if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
}
else if (isUserAttr) else if (isUserAttr)
{ {
int location = (attr - AttributeConsts.UserAttributeBase) / 16; int location = (attr - AttributeConsts.UserAttributeBase) / 16;

View file

@ -882,7 +882,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (src2 is AstOperand operand && operand.Type == OperandType.Constant) if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{ {
int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2);
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr: false, index)); bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0;
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index));
} }
else else
{ {

View file

@ -191,7 +191,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
break; break;
} }
if (context.Config.GpuAccessor.QueryTessCw()) bool tessCw = context.Config.GpuAccessor.QueryTessCw();
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
// We invert the front face on Vulkan backend, so we need to do that here aswell.
tessCw = !tessCw;
}
if (tessCw)
{ {
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw); context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw);
} }
@ -374,10 +382,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Store(context.GetLocalPointer(dest), source); context.Store(context.GetLocalPointer(dest), source);
} }
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
{
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true))
{ {
bool perPatch = dest.Type == OperandType.AttributePerPatch; bool perPatch = dest.Type == OperandType.AttributePerPatch;
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch))
{
AggregateType elemType; AggregateType elemType;
var elemPointer = perPatch var elemPointer = perPatch

View file

@ -306,18 +306,36 @@ namespace Ryujinx.Graphics.Shader.Decoders
for (int elemIndex = 0; elemIndex < count; elemIndex++) for (int elemIndex = 0; elemIndex < count; elemIndex++)
{ {
int attr = offset + elemIndex * 4; int attr = offset + elemIndex * 4;
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
if (perPatch)
{
if (attr >= AttributeConsts.UserAttributePerPatchBase && attr < AttributeConsts.UserAttributePerPatchEnd)
{
int userAttr = attr - AttributeConsts.UserAttributePerPatchBase;
int index = userAttr / 16;
if (isStore)
{
config.SetOutputUserAttributePerPatch(index);
}
else
{
config.SetInputUserAttributePerPatch(index);
}
}
}
else if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
{ {
int userAttr = attr - AttributeConsts.UserAttributeBase; int userAttr = attr - AttributeConsts.UserAttributeBase;
int index = userAttr / 16; int index = userAttr / 16;
if (isStore) if (isStore)
{ {
config.SetOutputUserAttribute(index, perPatch); config.SetOutputUserAttribute(index);
} }
else else
{ {
config.SetInputUserAttribute(index, (userAttr >> 2) & 3, perPatch); config.SetInputUserAttribute(index, (userAttr >> 2) & 3);
} }
} }

View file

@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(offset); context.FlagAttributeRead(offset);
if (op.O) if (op.O && CanLoadOutput(offset))
{ {
offset |= AttributeConsts.LoadOutputMask; offset |= AttributeConsts.LoadOutputMask;
} }
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(offset); context.FlagAttributeRead(offset);
if (op.O) if (op.O && CanLoadOutput(offset))
{ {
offset |= AttributeConsts.LoadOutputMask; offset |= AttributeConsts.LoadOutputMask;
} }
@ -241,6 +241,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
} }
private static bool CanLoadOutput(int attr)
{
return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
}
private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr) private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr)
{ {
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR) if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR)

View file

@ -97,9 +97,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant) if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
{ {
int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2); int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
{
context.Info.Outputs.Add(attrOffset);
}
else
{
context.Info.Inputs.Add(attrOffset); context.Info.Inputs.Add(attrOffset);
} }
} }
}
int sourcesCount = operation.SourcesCount; int sourcesCount = operation.SourcesCount;
int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0; int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0;

View file

@ -54,6 +54,9 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int UserAttributeBase = 0x80; public const int UserAttributeBase = 0x80;
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; 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 LoadOutputMask = 1 << 30;
public const int Mask = 0x3fffffff; public const int Mask = 0x3fffffff;

View file

@ -4,14 +4,8 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
struct AttributeInfo struct AttributeInfo
{ {
private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>() private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = 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) },
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 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.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
@ -29,8 +23,8 @@ namespace Ryujinx.Graphics.Shader.Translation
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 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.Vector | AggregateType.FP32) }, { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
@ -55,6 +49,16 @@ namespace Ryujinx.Graphics.Shader.Translation
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | 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 BaseValue { get; }
public int Value { get; } public int Value { get; }
public int Length { get; } public int Length { get; }
@ -76,6 +80,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return (Value - BaseValue) / 4; 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) public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
{ {
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex()) if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
@ -86,6 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return From(config, value, isOutAttr).IsValid; 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) public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
{ {
value &= ~3; value &= ~3;
@ -115,7 +129,24 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
return new AttributeInfo(value, 0, 1, AggregateType.FP32); return new AttributeInfo(value, 0, 1, AggregateType.FP32);
} }
else if (BuiltInAttributes.TryGetValue(value, out AttributeInfo info)) 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.Vector | AggregateType.FP32, false);
}
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
{ {
return info; return info;
} }

View file

@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int index = BitOperations.TrailingZeroCount(passthroughAttributes); int index = BitOperations.TrailingZeroCount(passthroughAttributes);
WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex); WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
Config.SetOutputUserAttribute(index, perPatch: false); Config.SetOutputUserAttribute(index);
passthroughAttributes &= ~(1 << index); passthroughAttributes &= ~(1 << index);
} }
@ -364,7 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0; bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0;
if (targetEnabled) if (targetEnabled)
{ {
Config.SetOutputUserAttribute(rtIndex, perPatch: false); Config.SetOutputUserAttribute(rtIndex);
regIndexBase += 4; regIndexBase += 4;
} }
} }

View file

@ -50,16 +50,16 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool NextUsesFixedFuncAttributes { get; private set; } public bool NextUsesFixedFuncAttributes { get; private set; }
public int UsedInputAttributes { get; private set; } public int UsedInputAttributes { get; private set; }
public int UsedOutputAttributes { get; private set; } public int UsedOutputAttributes { get; private set; }
public int UsedInputAttributesPerPatch { get; private set; } public HashSet<int> UsedInputAttributesPerPatch { get; }
public int UsedOutputAttributesPerPatch { get; private set; } public HashSet<int> UsedOutputAttributesPerPatch { get; }
public HashSet<int> NextUsedInputAttributesPerPatch { get; private set; }
public int PassthroughAttributes { get; private set; } public int PassthroughAttributes { get; private set; }
private int _nextUsedInputAttributes; private int _nextUsedInputAttributes;
private int _thisUsedInputAttributes; private int _thisUsedInputAttributes;
private Dictionary<int, int> _perPatchAttributeLocations;
public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 NextInputAttributesComponents { get; private set; }
public UInt128 ThisInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; }
public UInt128 NextInputAttributesPerPatchComponents { get; private set; }
public UInt128 ThisInputAttributesPerPatchComponents { get; private set; }
private int _usedConstantBuffers; private int _usedConstantBuffers;
private int _usedStorageBuffers; private int _usedStorageBuffers;
@ -122,6 +122,10 @@ namespace Ryujinx.Graphics.Shader.Translation
Stage = ShaderStage.Compute; Stage = ShaderStage.Compute;
GpuAccessor = gpuAccessor; GpuAccessor = gpuAccessor;
Options = options; Options = options;
UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>();
_usedTextures = new Dictionary<TextureInfo, TextureMeta>(); _usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>(); _usedImages = new Dictionary<TextureInfo, TextureMeta>();
} }
@ -244,14 +248,7 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedOutputAttributes |= 1 << index; UsedOutputAttributes |= 1 << index;
} }
public void SetInputUserAttribute(int index, int component, bool perPatch) public void SetInputUserAttribute(int index, int component)
{
if (perPatch)
{
UsedInputAttributesPerPatch |= 1 << index;
ThisInputAttributesPerPatchComponents |= UInt128.Pow2(index * 4 + component);
}
else
{ {
int mask = 1 << index; int mask = 1 << index;
@ -259,34 +256,63 @@ namespace Ryujinx.Graphics.Shader.Translation
_thisUsedInputAttributes |= mask; _thisUsedInputAttributes |= mask;
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component);
} }
public void SetInputUserAttributePerPatch(int index)
{
UsedInputAttributesPerPatch.Add(index);
} }
public void SetOutputUserAttribute(int index, bool perPatch) public void SetOutputUserAttribute(int index)
{
if (perPatch)
{
UsedOutputAttributesPerPatch |= 1 << index;
}
else
{ {
UsedOutputAttributes |= 1 << index; UsedOutputAttributes |= 1 << index;
} }
public void SetOutputUserAttributePerPatch(int index)
{
UsedOutputAttributesPerPatch.Add(index);
} }
public void MergeFromtNextStage(ShaderConfig config) public void MergeFromtNextStage(ShaderConfig config)
{ {
NextInputAttributesComponents = config.ThisInputAttributesComponents; NextInputAttributesComponents = config.ThisInputAttributesComponents;
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents; NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch;
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
if (UsedOutputAttributesPerPatch.Count != 0)
{
// Regular and per-patch input/output locations can't overlap,
// so we must assign on our location using unused regular input/output locations.
Dictionary<int, int> locationsMap = new Dictionary<int, int>();
int freeMask = ~UsedOutputAttributes;
foreach (int attr in UsedOutputAttributesPerPatch)
{
int location = BitOperations.TrailingZeroCount(freeMask);
if (location == 32)
{
config.GpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}.");
break;
}
locationsMap.Add(attr, location);
freeMask &= ~(1 << location);
}
// Both stages must agree on the locations, so use the same "map" for both.
_perPatchAttributeLocations = locationsMap;
config._perPatchAttributeLocations = locationsMap;
}
if (config.Stage != ShaderStage.Fragment) if (config.Stage != ShaderStage.Fragment)
{ {
LastInVertexPipeline = false; LastInVertexPipeline = false;
} }
} }
public void MergeOutputUserAttributes(int mask, int maskPerPatch) public void MergeOutputUserAttributes(int mask, IEnumerable<int> perPatch)
{ {
_nextUsedInputAttributes = mask; _nextUsedInputAttributes = mask;
@ -297,10 +323,20 @@ namespace Ryujinx.Graphics.Shader.Translation
else else
{ {
UsedOutputAttributes |= mask; UsedOutputAttributes |= mask;
UsedOutputAttributesPerPatch |= maskPerPatch; UsedOutputAttributesPerPatch.UnionWith(perPatch);
} }
} }
public int GetPerPatchAttributeLocation(int index)
{
if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location))
{
return index;
}
return location;
}
public bool IsUsedOutputAttribute(int attr) public bool IsUsedOutputAttribute(int attr)
{ {
// The check for fixed function attributes on the next stage is conservative, // The check for fixed function attributes on the next stage is conservative,

View file

@ -204,14 +204,12 @@ namespace Ryujinx.Graphics.Shader.Translation
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false); InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
} }
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents; if (context.Config.NextUsedInputAttributesPerPatch != null)
while (usedAttributesPerPatch != UInt128.Zero)
{ {
int index = usedAttributesPerPatch.TrailingZeroCount(); foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x))
{
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true); InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true);
}
usedAttributesPerPatch &= ~UInt128.Pow2(index);
} }
if (config.NextUsesFixedFuncAttributes) if (config.NextUsesFixedFuncAttributes)
@ -236,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int c = 0; c < 4; c++) for (int c = 0; c < 4; c++)
{ {
int attrOffset = baseAttr + c * 4; int attrOffset = baseAttr + c * 4;
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f)); InitializeOutputComponent(context, attrOffset, perPatch);
} }
} }

View file

@ -1,6 +1,7 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
using static Ryujinx.Graphics.Shader.Translation.Translator; using static Ryujinx.Graphics.Shader.Translation.Translator;
@ -137,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (other != null) if (other != null)
{ {
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0); other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart); FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);