Initial support for shader attribute indexing (#2546)
* Initial support for shader attribute indexing * Support output indexing too, other improvements * Fix order * Address feedback
This commit is contained in:
parent
ec3e848d79
commit
ee1038e542
22 changed files with 298 additions and 86 deletions
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const ulong ShaderCodeGenVersion = 2542;
|
private const ulong ShaderCodeGenVersion = 2546;
|
||||||
|
|
||||||
// Progress reporting helpers
|
// Progress reporting helpers
|
||||||
private volatile int _shaderCount;
|
private volatile int _shaderCount;
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen
|
|
||||||
{
|
|
||||||
static class Constants
|
|
||||||
{
|
|
||||||
public const int MaxShaderStorageBuffers = 16;
|
|
||||||
|
|
||||||
public const int ConstantBufferSize = 0x10000; // In bytes
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -402,14 +402,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
int usedAttribtes = context.Config.UsedInputAttributes;
|
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing))
|
||||||
while (usedAttribtes != 0)
|
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttribtes);
|
string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty;
|
||||||
|
|
||||||
|
context.AppendLine($"layout (location = 0) in vec4 {DefaultNames.IAttributePrefix}{suffix}[{Constants.MaxAttributes}];");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int usedAttributes = context.Config.UsedInputAttributes;
|
||||||
|
while (usedAttributes != 0)
|
||||||
|
{
|
||||||
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
|
||||||
DeclareInputAttribute(context, info, index);
|
DeclareInputAttribute(context, info, index);
|
||||||
|
|
||||||
usedAttribtes &= ~(1 << index);
|
usedAttributes &= ~(1 << index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,14 +457,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
int usedAttribtes = context.Config.UsedOutputAttributes;
|
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
|
||||||
while (usedAttribtes != 0)
|
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttribtes);
|
context.AppendLine($"layout (location = 0) out vec4 {DefaultNames.OAttributePrefix}[{Constants.MaxAttributes}];");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int usedAttributes = context.Config.UsedOutputAttributes;
|
||||||
|
while (usedAttributes != 0)
|
||||||
|
{
|
||||||
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
|
||||||
DeclareOutputAttribute(context, index);
|
DeclareOutputAttribute(context, index);
|
||||||
|
|
||||||
usedAttribtes &= ~(1 << index);
|
usedAttributes &= ~(1 << index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute)
|
if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute)
|
||||||
{
|
{
|
||||||
dest = OperandManager.GetOutAttributeName(operand, context.Config);
|
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -161,6 +161,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
case Instruction.PackHalf2x16:
|
case Instruction.PackHalf2x16:
|
||||||
return PackHalf2x16(context, operation);
|
return PackHalf2x16(context, operation);
|
||||||
|
|
||||||
|
case Instruction.StoreAttribute:
|
||||||
|
return StoreAttribute(context, operation);
|
||||||
|
|
||||||
case Instruction.StoreLocal:
|
case Instruction.StoreLocal:
|
||||||
return StoreLocal(context, operation);
|
return StoreLocal(context, operation);
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
|
Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
|
||||||
Add(Instruction.Sine, InstType.CallUnary, "sin");
|
Add(Instruction.Sine, InstType.CallUnary, "sin");
|
||||||
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
||||||
|
Add(Instruction.StoreAttribute, InstType.Special);
|
||||||
Add(Instruction.StoreLocal, InstType.Special);
|
Add(Instruction.StoreLocal, InstType.Special);
|
||||||
Add(Instruction.StoreShared, InstType.Special);
|
Add(Instruction.StoreShared, InstType.Special);
|
||||||
Add(Instruction.StoreStorage, InstType.Special);
|
Add(Instruction.StoreStorage, InstType.Special);
|
||||||
|
|
|
@ -137,15 +137,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
{
|
{
|
||||||
IAstNode src1 = operation.GetSource(0);
|
IAstNode src1 = operation.GetSource(0);
|
||||||
IAstNode src2 = operation.GetSource(1);
|
IAstNode src2 = operation.GetSource(1);
|
||||||
|
IAstNode src3 = operation.GetSource(2);
|
||||||
|
|
||||||
if (!(src1 is AstOperand attr) || attr.Type != OperandType.Attribute)
|
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("First source of LoadAttribute must be a attribute.");
|
throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
string indexExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
string indexExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2));
|
||||||
|
|
||||||
return OperandManager.GetAttributeName(attr, context.Config, isOutAttr: false, indexExpr);
|
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
return OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: false, indexExpr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||||
|
attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
||||||
|
return OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: false, indexExpr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
||||||
|
@ -154,16 +164,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
IAstNode src2 = operation.GetSource(1);
|
IAstNode src2 = operation.GetSource(1);
|
||||||
|
|
||||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||||
|
|
||||||
offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
||||||
|
|
||||||
var config = context.Config;
|
var config = context.Config;
|
||||||
bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
||||||
|
|
||||||
if (src1 is AstOperand oper && oper.Type == OperandType.Constant)
|
if (src1 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||||
{
|
{
|
||||||
bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
|
bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
|
||||||
return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
|
return OperandManager.GetConstantBufferName(operand.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -250,6 +259,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
|
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string StoreAttribute(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
IAstNode src1 = operation.GetSource(0);
|
||||||
|
IAstNode src2 = operation.GetSource(1);
|
||||||
|
IAstNode src3 = operation.GetSource(2);
|
||||||
|
|
||||||
|
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string attrName;
|
||||||
|
|
||||||
|
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
attrName = OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||||
|
attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
||||||
|
attrName = OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
string value = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2));
|
||||||
|
return $"{attrName} = {value}";
|
||||||
|
}
|
||||||
|
|
||||||
public static string StoreLocal(CodeGenContext context, AstOperation operation)
|
public static string StoreLocal(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
|
return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
return operand.Type switch
|
return operand.Type switch
|
||||||
{
|
{
|
||||||
OperandType.Argument => GetArgumentName(operand.Value),
|
OperandType.Argument => GetArgumentName(operand.Value),
|
||||||
OperandType.Attribute => GetAttributeName(operand, config),
|
OperandType.Attribute => GetAttributeName(operand.Value, config),
|
||||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||||
OperandType.ConstantBuffer => GetConstantBufferName(
|
OperandType.ConstantBuffer => GetConstantBufferName(
|
||||||
operand.CbufSlot,
|
operand.CbufSlot,
|
||||||
|
@ -142,15 +142,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetOutAttributeName(AstOperand attr, ShaderConfig config)
|
public static string GetOutAttributeName(int value, ShaderConfig config)
|
||||||
{
|
{
|
||||||
return GetAttributeName(attr, config, isOutAttr: true);
|
return GetAttributeName(value, config, isOutAttr: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetAttributeName(AstOperand attr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
public static string GetAttributeName(int value, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
||||||
{
|
{
|
||||||
int value = attr.Value;
|
|
||||||
|
|
||||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||||
|
@ -161,7 +159,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
? DefaultNames.OAttributePrefix
|
? DefaultNames.OAttributePrefix
|
||||||
: DefaultNames.IAttributePrefix;
|
: DefaultNames.IAttributePrefix;
|
||||||
|
|
||||||
if ((config.Options.Flags & TranslationFlags.Feedback) != 0)
|
if (config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing))
|
||||||
|
{
|
||||||
|
string name = prefix;
|
||||||
|
|
||||||
|
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
||||||
|
{
|
||||||
|
name += $"[{indexExpr}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return name + $"[{(value >> 4)}]." + swzMask;
|
||||||
|
}
|
||||||
|
else if (config.Options.Flags.HasFlag(TranslationFlags.Feedback))
|
||||||
{
|
{
|
||||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||||
|
|
||||||
|
@ -231,6 +240,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetAttributeName(string attrExpr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
||||||
|
{
|
||||||
|
string name = isOutAttr
|
||||||
|
? DefaultNames.OAttributePrefix
|
||||||
|
: DefaultNames.IAttributePrefix;
|
||||||
|
|
||||||
|
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
||||||
|
{
|
||||||
|
name += $"[{indexExpr}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]";
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
||||||
{
|
{
|
||||||
if (cbIndexable)
|
if (cbIndexable)
|
||||||
|
@ -313,13 +336,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node)
|
public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node)
|
||||||
{
|
{
|
||||||
if (node is AstOperation operation)
|
if (node is AstOperation operation)
|
||||||
|
{
|
||||||
|
if (operation.Inst == Instruction.LoadAttribute)
|
||||||
{
|
{
|
||||||
// Load attribute basically just returns the attribute value.
|
// Load attribute basically just returns the attribute value.
|
||||||
// Some built-in attributes may have different types, so we need
|
// Some built-in attributes may have different types, so we need
|
||||||
// to return the type based on the attribute that is being read.
|
// to return the type based on the attribute that is being read.
|
||||||
if (operation.Inst == Instruction.LoadAttribute)
|
if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant)
|
||||||
{
|
{
|
||||||
return GetOperandVarType((AstOperand)operation.GetSource(0));
|
if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr))
|
||||||
|
{
|
||||||
|
return builtInAttr.Type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OperandInfo.GetVarType(OperandType.Attribute);
|
||||||
}
|
}
|
||||||
else if (operation.Inst == Instruction.Call)
|
else if (operation.Inst == Instruction.Call)
|
||||||
{
|
{
|
||||||
|
|
10
Ryujinx.Graphics.Shader/Constants.cs
Normal file
10
Ryujinx.Graphics.Shader/Constants.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.Graphics.Shader
|
||||||
|
{
|
||||||
|
static class Constants
|
||||||
|
{
|
||||||
|
public const int ConstantBufferSize = 0x10000; // In bytes
|
||||||
|
|
||||||
|
public const int MaxAttributes = 16;
|
||||||
|
public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes));
|
||||||
|
}
|
||||||
|
}
|
|
@ -281,6 +281,33 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
|
||||||
// Populate used attributes.
|
// Populate used attributes.
|
||||||
if (op is IOpCodeAttribute opAttr)
|
if (op is IOpCodeAttribute opAttr)
|
||||||
|
{
|
||||||
|
SetUserAttributeUses(config, opAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.OpCodes.Add(op);
|
||||||
|
}
|
||||||
|
while (!IsControlFlowChange(block.GetLastOp()));
|
||||||
|
|
||||||
|
block.EndAddress = address;
|
||||||
|
|
||||||
|
block.UpdatePushOps();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetUserAttributeUses(ShaderConfig config, IOpCodeAttribute opAttr)
|
||||||
|
{
|
||||||
|
if (opAttr.Indexed)
|
||||||
|
{
|
||||||
|
if (opAttr.Emitter == InstEmit.Ast)
|
||||||
|
{
|
||||||
|
config.SetAllOutputUserAttributes();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.SetAllInputUserAttributes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++)
|
for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++)
|
||||||
{
|
{
|
||||||
|
@ -289,7 +316,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
{
|
{
|
||||||
int index = (attr - AttributeConsts.UserAttributeBase) / 16;
|
int index = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||||
|
|
||||||
if (op.Emitter == InstEmit.Ast)
|
if (opAttr.Emitter == InstEmit.Ast)
|
||||||
{
|
{
|
||||||
config.SetOutputUserAttribute(index);
|
config.SetOutputUserAttribute(index);
|
||||||
}
|
}
|
||||||
|
@ -300,14 +327,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block.OpCodes.Add(op);
|
|
||||||
}
|
|
||||||
while (!IsControlFlowChange(block.GetLastOp()));
|
|
||||||
|
|
||||||
block.EndAddress = address;
|
|
||||||
|
|
||||||
block.UpdatePushOps();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsUnconditionalBranch(OpCode opCode)
|
private static bool IsUnconditionalBranch(OpCode opCode)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
namespace Ryujinx.Graphics.Shader.Decoders
|
namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
{
|
{
|
||||||
interface IOpCodeAttribute
|
interface IOpCodeAttribute : IOpCode
|
||||||
{
|
{
|
||||||
int AttributeOffset { get; }
|
int AttributeOffset { get; }
|
||||||
int Count { get; }
|
int Count { get; }
|
||||||
|
bool Indexed { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
24
Ryujinx.Graphics.Shader/Decoders/OpCodeAl2p.cs
Normal file
24
Ryujinx.Graphics.Shader/Decoders/OpCodeAl2p.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using Ryujinx.Graphics.Shader.Instructions;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeAl2p : OpCode, IOpCodeRd, IOpCodeRa
|
||||||
|
{
|
||||||
|
public Register Rd { get; }
|
||||||
|
public Register Ra { get; }
|
||||||
|
public Register Predicate44 { get; }
|
||||||
|
|
||||||
|
public int Immediate { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeAl2p(emitter, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeAl2p(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||||
|
{
|
||||||
|
Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
|
||||||
|
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
|
||||||
|
Predicate44 = new Register(opCode.Extract(44, 3), RegisterType.Predicate);
|
||||||
|
|
||||||
|
Immediate = ((int)opCode << 1) >> 21;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,13 +5,18 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
class OpCodeAttribute : OpCodeAluReg, IOpCodeAttribute
|
class OpCodeAttribute : OpCodeAluReg, IOpCodeAttribute
|
||||||
{
|
{
|
||||||
public int AttributeOffset { get; }
|
public int AttributeOffset { get; }
|
||||||
|
public bool Patch { get; }
|
||||||
public int Count { get; }
|
public int Count { get; }
|
||||||
|
|
||||||
|
public bool Phys => !Patch && AttributeOffset == 0 && !Ra.IsRZ;
|
||||||
|
public bool Indexed => Phys;
|
||||||
|
|
||||||
public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeAttribute(emitter, address, opCode);
|
public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeAttribute(emitter, address, opCode);
|
||||||
|
|
||||||
public OpCodeAttribute(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
public OpCodeAttribute(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
|
||||||
{
|
{
|
||||||
AttributeOffset = opCode.Extract(20, 10);
|
AttributeOffset = opCode.Extract(20, 10);
|
||||||
|
Patch = opCode.Extract(31);
|
||||||
Count = opCode.Extract(47, 2) + 1;
|
Count = opCode.Extract(47, 2) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
public int AttributeOffset { get; }
|
public int AttributeOffset { get; }
|
||||||
public int Count => 1;
|
public int Count => 1;
|
||||||
|
|
||||||
|
public bool Idx { get; }
|
||||||
|
public bool Indexed => Idx;
|
||||||
|
|
||||||
public InterpolationMode Mode { get; }
|
public InterpolationMode Mode { get; }
|
||||||
|
|
||||||
public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeIpa(emitter, address, opCode);
|
public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeIpa(emitter, address, opCode);
|
||||||
|
@ -15,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
{
|
{
|
||||||
AttributeOffset = opCode.Extract(28, 10);
|
AttributeOffset = opCode.Extract(28, 10);
|
||||||
|
|
||||||
|
Idx = opCode.Extract(38);
|
||||||
|
|
||||||
Saturate = opCode.Extract(51);
|
Saturate = opCode.Extract(51);
|
||||||
|
|
||||||
Mode = (InterpolationMode)opCode.Extract(54, 2);
|
Mode = (InterpolationMode)opCode.Extract(54, 2);
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
_opCodes = new TableEntry[1 << EncodingBits];
|
_opCodes = new TableEntry[1 << EncodingBits];
|
||||||
|
|
||||||
#region Instructions
|
#region Instructions
|
||||||
|
Set("1110111110100x", InstEmit.Al2p, OpCodeAl2p.Create);
|
||||||
Set("1110111111011x", InstEmit.Ald, OpCodeAttribute.Create);
|
Set("1110111111011x", InstEmit.Ald, OpCodeAttribute.Create);
|
||||||
Set("1110111111110x", InstEmit.Ast, OpCodeAttribute.Create);
|
Set("1110111111110x", InstEmit.Ast, OpCodeAttribute.Create);
|
||||||
Set("11101101xxxxxx", InstEmit.Atom, OpCodeAtom.Create);
|
Set("11101101xxxxxx", InstEmit.Atom, OpCodeAtom.Create);
|
||||||
|
|
|
@ -15,6 +15,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
Shared
|
Shared
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Al2p(EmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeAl2p op = (OpCodeAl2p)context.CurrOp;
|
||||||
|
|
||||||
|
if (op.Rd.IsRZ)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(Register(op.Rd), context.IAdd(Register(op.Ra), Const(op.Immediate)));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Ald(EmitterContext context)
|
public static void Ald(EmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
|
OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
|
||||||
|
@ -30,11 +42,31 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op.Phys)
|
||||||
|
{
|
||||||
|
Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
|
||||||
|
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
||||||
|
|
||||||
|
context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
|
||||||
|
|
||||||
|
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
||||||
|
}
|
||||||
|
else if (op.Rc.IsRZ)
|
||||||
|
{
|
||||||
Operand src = Attribute(op.AttributeOffset + index * 4);
|
Operand src = Attribute(op.AttributeOffset + index * 4);
|
||||||
|
|
||||||
context.FlagAttributeRead(src.Value);
|
context.FlagAttributeRead(src.Value);
|
||||||
|
|
||||||
context.Copy(Register(rd), context.LoadAttribute(src, primVertex));
|
context.Copy(Register(rd), src);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand src = Const(op.AttributeOffset + index * 4);
|
||||||
|
|
||||||
|
context.FlagAttributeRead(src.Value);
|
||||||
|
|
||||||
|
context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +83,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
|
Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
|
||||||
|
|
||||||
|
if (op.Phys)
|
||||||
|
{
|
||||||
|
Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
|
||||||
|
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
||||||
|
|
||||||
|
context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
|
||||||
|
|
||||||
|
context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Operand dest = Attribute(op.AttributeOffset + index * 4);
|
Operand dest = Attribute(op.AttributeOffset + index * 4);
|
||||||
|
|
||||||
context.FlagAttributeWritten(dest.Value);
|
context.FlagAttributeWritten(dest.Value);
|
||||||
|
@ -58,6 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(dest, Register(rd));
|
context.Copy(dest, Register(rd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Atom(EmitterContext context)
|
public static void Atom(EmitterContext context)
|
||||||
{
|
{
|
||||||
|
@ -136,7 +180,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.FlagAttributeRead(op.AttributeOffset);
|
context.FlagAttributeRead(op.AttributeOffset);
|
||||||
|
|
||||||
Operand res = Attribute(op.AttributeOffset);
|
Operand res;
|
||||||
|
|
||||||
|
if (op.Idx)
|
||||||
|
{
|
||||||
|
Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
|
||||||
|
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
||||||
|
|
||||||
|
res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0));
|
||||||
|
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
||||||
|
|
||||||
|
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = Attribute(op.AttributeOffset);
|
||||||
|
|
||||||
if (op.AttributeOffset >= AttributeConsts.UserAttributeBase &&
|
if (op.AttributeOffset >= AttributeConsts.UserAttributeBase &&
|
||||||
op.AttributeOffset < AttributeConsts.UserAttributeEnd)
|
op.AttributeOffset < AttributeConsts.UserAttributeEnd)
|
||||||
|
@ -148,6 +206,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (op.Mode == InterpolationMode.Default)
|
if (op.Mode == InterpolationMode.Default)
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,6 +108,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
ShuffleXor,
|
ShuffleXor,
|
||||||
Sine,
|
Sine,
|
||||||
SquareRoot,
|
SquareRoot,
|
||||||
|
StoreAttribute,
|
||||||
StoreGlobal,
|
StoreGlobal,
|
||||||
StoreLocal,
|
StoreLocal,
|
||||||
StoreShared,
|
StoreShared,
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
Add(Instruction.ImageLoad, VariableType.F32);
|
Add(Instruction.ImageLoad, VariableType.F32);
|
||||||
Add(Instruction.ImageStore, VariableType.None);
|
Add(Instruction.ImageStore, VariableType.None);
|
||||||
Add(Instruction.IsNan, VariableType.Bool, VariableType.F32);
|
Add(Instruction.IsNan, VariableType.Bool, VariableType.F32);
|
||||||
Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32);
|
Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32, VariableType.S32);
|
||||||
Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32);
|
Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32);
|
||||||
Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32);
|
Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32);
|
||||||
Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32);
|
Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32);
|
||||||
|
@ -115,6 +115,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
|
Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
|
||||||
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||||
|
Add(Instruction.StoreAttribute, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32);
|
||||||
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
|
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
|
||||||
Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.U32);
|
Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.U32);
|
||||||
Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32);
|
Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32);
|
||||||
|
|
|
@ -511,9 +511,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.IsNan, Local(), a);
|
return context.Add(Instruction.IsNan, Local(), a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b)
|
public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.LoadAttribute, Local(), a, b);
|
return context.Add(Instruction.LoadAttribute, Local(), a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
|
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
|
||||||
|
@ -617,6 +617,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
|
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Operand StoreAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.StoreAttribute, null, a, b, c);
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
|
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.StoreGlobal, null, a, b, c);
|
return context.Add(Instruction.StoreGlobal, null, a, b, c);
|
||||||
|
|
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
Bindless = 1 << 2,
|
Bindless = 1 << 2,
|
||||||
InstanceId = 1 << 3,
|
InstanceId = 1 << 3,
|
||||||
CbIndexing = 1 << 4
|
CbIndexing = 1 << 4,
|
||||||
|
IaIndexing = 1 << 5,
|
||||||
|
OaIndexing = 1 << 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAllInputUserAttributes()
|
||||||
|
{
|
||||||
|
UsedInputAttributes |= Constants.AllAttributesMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAllOutputUserAttributes()
|
||||||
|
{
|
||||||
|
UsedOutputAttributes |= Constants.AllAttributesMask;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetClipDistanceWritten(int index)
|
public void SetClipDistanceWritten(int index)
|
||||||
{
|
{
|
||||||
ClipDistancesWritten |= (byte)(1 << index);
|
ClipDistancesWritten |= (byte)(1 << index);
|
||||||
|
|
|
@ -102,21 +102,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4];
|
Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4];
|
||||||
|
|
||||||
if (temp != null)
|
if (temp != null)
|
||||||
{
|
|
||||||
// TODO: LoadAttribute should accept any integer value as first argument,
|
|
||||||
// then we don't need special case here. Right now it expects the first
|
|
||||||
// operand to be of type "attribute".
|
|
||||||
if ((operation.Inst & Instruction.Mask) == Instruction.LoadAttribute)
|
|
||||||
{
|
|
||||||
operation.TurnIntoCopy(temp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
operation.SetSource(srcIndex, temp);
|
operation.SetSource(srcIndex, temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ops.Add(operation);
|
ops.Add(operation);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue