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:
gdkchan 2021-08-26 20:44:47 -03:00 committed by GitHub
parent ec3e848d79
commit ee1038e542
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 298 additions and 86 deletions

View file

@ -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;

View file

@ -1,9 +0,0 @@
namespace Ryujinx.Graphics.Shader.CodeGen
{
static class Constants
{
public const int MaxShaderStorageBuffers = 16;
public const int ConstantBufferSize = 0x10000; // In bytes
}
}

View file

@ -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;
DeclareInputAttribute(context, info, index); 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);
usedAttribtes &= ~(1 << index); DeclareInputAttribute(context, info, 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);
}
} }
} }

View file

@ -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
{ {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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)
@ -314,12 +337,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
if (node is AstOperation operation) if (node is AstOperation operation)
{ {
// Load attribute basically just returns the attribute value.
// Some built-in attributes may have different types, so we need
// to return the type based on the attribute that is being read.
if (operation.Inst == Instruction.LoadAttribute) if (operation.Inst == Instruction.LoadAttribute)
{ {
return GetOperandVarType((AstOperand)operation.GetSource(0)); // Load attribute basically just returns the attribute value.
// Some built-in attributes may have different types, so we need
// to return the type based on the attribute that is being read.
if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant)
{
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)
{ {

View 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));
}
}

View file

@ -282,23 +282,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
// Populate used attributes. // Populate used attributes.
if (op is IOpCodeAttribute opAttr) if (op is IOpCodeAttribute opAttr)
{ {
for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++) SetUserAttributeUses(config, opAttr);
{
int attr = opAttr.AttributeOffset + elemIndex * 4;
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
{
int index = (attr - AttributeConsts.UserAttributeBase) / 16;
if (op.Emitter == InstEmit.Ast)
{
config.SetOutputUserAttribute(index);
}
else
{
config.SetInputUserAttribute(index);
}
}
}
} }
block.OpCodes.Add(op); block.OpCodes.Add(op);
@ -310,6 +294,41 @@ namespace Ryujinx.Graphics.Shader.Decoders
block.UpdatePushOps(); 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++)
{
int attr = opAttr.AttributeOffset + elemIndex * 4;
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
{
int index = (attr - AttributeConsts.UserAttributeBase) / 16;
if (opAttr.Emitter == InstEmit.Ast)
{
config.SetOutputUserAttribute(index);
}
else
{
config.SetInputUserAttribute(index);
}
}
}
}
}
private static bool IsUnconditionalBranch(OpCode opCode) private static bool IsUnconditionalBranch(OpCode opCode)
{ {
return IsUnconditional(opCode) && IsControlFlowChange(opCode); return IsUnconditional(opCode) && IsControlFlowChange(opCode);

View file

@ -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; }
} }
} }

View 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;
}
}
}

View file

@ -4,14 +4,19 @@ namespace Ryujinx.Graphics.Shader.Decoders
{ {
class OpCodeAttribute : OpCodeAluReg, IOpCodeAttribute class OpCodeAttribute : OpCodeAluReg, IOpCodeAttribute
{ {
public int AttributeOffset { get; } public int AttributeOffset { get; }
public int Count { get; } public bool Patch { 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;
} }
} }

View file

@ -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);

View file

@ -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);

View file

@ -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;
} }
Operand src = Attribute(op.AttributeOffset + index * 4); if (op.Phys)
{
Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
context.FlagAttributeRead(src.Value); context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
context.Copy(Register(rd), context.LoadAttribute(src, primVertex)); context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
}
else if (op.Rc.IsRZ)
{
Operand src = Attribute(op.AttributeOffset + index * 4);
context.FlagAttributeRead(src.Value);
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,11 +83,23 @@ 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);
Operand dest = Attribute(op.AttributeOffset + index * 4); if (op.Phys)
{
Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
context.FlagAttributeWritten(dest.Value); context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
context.Copy(dest, Register(rd)); context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
}
else
{
Operand dest = Attribute(op.AttributeOffset + index * 4);
context.FlagAttributeWritten(dest.Value);
context.Copy(dest, Register(rd));
}
} }
} }
@ -136,16 +180,31 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(op.AttributeOffset); context.FlagAttributeRead(op.AttributeOffset);
Operand res = Attribute(op.AttributeOffset); Operand res;
if (op.AttributeOffset >= AttributeConsts.UserAttributeBase && if (op.Idx)
op.AttributeOffset < AttributeConsts.UserAttributeEnd)
{ {
int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4; Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) 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 &&
op.AttributeOffset < AttributeConsts.UserAttributeEnd)
{ {
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4;
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
{
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
}
} }
} }

View file

@ -108,6 +108,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ShuffleXor, ShuffleXor,
Sine, Sine,
SquareRoot, SquareRoot,
StoreAttribute,
StoreGlobal, StoreGlobal,
StoreLocal, StoreLocal,
StoreShared, StoreShared,

View file

@ -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);

View file

@ -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);

View file

@ -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
} }
} }

View file

@ -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);

View file

@ -103,17 +103,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (temp != null) if (temp != null)
{ {
// TODO: LoadAttribute should accept any integer value as first argument, operation.SetSource(srcIndex, temp);
// 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);
}
} }
} }
} }