Compare commits

...

6 Commits

Author SHA1 Message Date
gdkchan
9c2500de5f Fix incorrect tessellation inputs/outputs (#3728)
* Fix incorrect tessellation inputs/outputs

* Shader cache version bump
2022-10-01 02:35:52 -03:00
gdkchan
dbe43c1719 Fix SSL GetCertificates with certificate ID set to All (#3727)
* Fix SSL GetCertificates with certificate ID set to All

* Fix last entry status value
2022-09-29 12:45:25 -03:00
riperiperi
f502cfaf62 Vulkan: Zero blend state when disabled or write mask is 0 (#3719)
* Zero blend state when disabled or write mask is 0

Any difference in the blend state when blend is disabled is meaningless, but Ryujinx would compare different disabled blends and compile them as separate pipelines. This change ensures that all pipelines where blend state is meaningless record it as such, which avoids compiling a bunch of pipelines that are essentially identical.

The NVIDIA driver is pretty forgiving when it comes to silly pipeline misses like this, but other drivers don't offer the same level of kindness.

This should reduce stuttering on those drivers, and might improve overall performance very slightly due to less pipeline variants being in the hash table.

* Fix blend possibly being wrong when an attachment is unmasked
2022-09-29 12:32:49 -03:00
gdkchan
1fd5cf2b4a Fix ListOpenContextStoredUsers and stub LoadOpenContext (#3718)
* Fix ListOpenContextStoredUsers and stub LoadOpenContext

* Remove nonsensical comment
2022-09-27 21:24:52 -03:00
LDj3SNuD
814f75142e Fpsr and Fpcr freed. (#3701)
* Implemented in IR the managed methods of the Saturating region ...

... of the SoftFallback class (the SatQ ones).

The need to natively manage the Fpcr and Fpsr system registers is still a fact.

Contributes to https://github.com/Ryujinx/Ryujinx/issues/2917 ; I will open another PR to implement in Intrinsics-branchless the methods of the Saturation region as well (the SatXXXToXXX ones).

All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq.

* Ptc.InternalVersion = 3665

* Addressed PR feedback.

* Implemented in IR the managed methods of the ShlReg region of the SoftFallback class.

It also includes the last two SatQ ones (following up on https://github.com/Ryujinx/Ryujinx/pull/3665).

All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq.

* Fpsr and Fpcr freed.

Handling/isolation of Fpsr and Fpcr via register for IR and via memory for Tests and Threads, with synchronization to context exchanges (explicit for SoftFloat); without having to call managed methods. Thanks to the inlining work of the previous two PRs and others in this.

Tests performed locally in both release and debug modes, in both lowcq and highcq, with FastFP to true and false (explicit FP tests included). Tested with the title Tony Hawk's PS.

Depends on shlreg.

* Update InstEmitSimdHelper.cs

* De-magic Masks.

Remove the Stride and Len flags; Fpsr.NZCV are A32 only, then moved to Fpscr: this leads to emitting less IR in reference to Get/Set Fpsr/Fpcr/Fpscr methods in reference to Mrs/Msr (A64) and Vmrs/Vmsr (A32) instructions.

* Addressed PR feedback.
2022-09-20 18:55:13 -03:00
riperiperi
4c0eb91d7e Convert Quads to Triangles in Vulkan (#3715)
* Add Index Buffer conversion for quads to Vulkan

Also adds a reusable repeating pattern index buffer to use for non-indexed
draws, and generalizes the conversion cache for buffers.

* Fix some issues

* End render pass before conversion

* Resume transform feedback after we ensure we're in a pass.

* Always generate UInt32 type indices for topology conversion

* No it's not.

* Remove unused code

* Rely on TopologyRemap to convert quads to tris.

* Remove double newline

* Ensure render pass ends before stride or I8 conversion
2022-09-20 18:38:48 -03:00
54 changed files with 1241 additions and 662 deletions

View File

@@ -726,7 +726,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
{
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)), op1, op2);
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum), op1, op2);
});
}
}
@@ -774,7 +774,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
{
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)), op1, op2);
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2);
});
}
}
@@ -900,7 +900,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
{
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)), op1, op2);
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum), op1, op2);
});
}
}
@@ -948,7 +948,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
{
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)), op1, op2);
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2);
});
}
}
@@ -1633,37 +1633,17 @@ namespace ARMeilleure.Instructions
public static void Frinti_S(ArmEmitterContext context)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
EmitScalarUnaryOpF(context, (op1) =>
{
if (op.Size == 0)
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
}
else /* if (op.Size == 1) */
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
}
return EmitRoundByRMode(context, op1);
});
}
public static void Frinti_V(ArmEmitterContext context)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
int sizeF = op.Size & 1;
EmitVectorUnaryOpF(context, (op1) =>
{
if (sizeF == 0)
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
}
else /* if (sizeF == 1) */
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
}
return EmitRoundByRMode(context, op1);
});
}
@@ -1759,37 +1739,17 @@ namespace ARMeilleure.Instructions
public static void Frintx_S(ArmEmitterContext context)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
EmitScalarUnaryOpF(context, (op1) =>
{
if (op.Size == 0)
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
}
else /* if (op.Size == 1) */
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
}
return EmitRoundByRMode(context, op1);
});
}
public static void Frintx_V(ArmEmitterContext context)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
int sizeF = op.Size & 1;
EmitVectorUnaryOpF(context, (op1) =>
{
if (sizeF == 0)
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
}
else /* if (sizeF == 1) */
{
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
}
return EmitRoundByRMode(context, op1);
});
}

View File

@@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
@@ -178,37 +177,20 @@ namespace ARMeilleure.Instructions
private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
{
Operand one = Const(1);
if (zero)
{
EmitVectorUnaryOpF32(context, (m) =>
{
OperandType type = m.Type;
Operand zeroOp = m.Type == OperandType.FP64 ? ConstF(0.0d) : ConstF(0.0f);
if (type == OperandType.FP64)
{
return context.Call(typeof(SoftFloat64).GetMethod(name), m, ConstF(0.0d), one);
}
else
{
return context.Call(typeof(SoftFloat32).GetMethod(name), m, ConstF(0.0f), one);
}
return EmitSoftFloatCallDefaultFpscr(context, name, m, zeroOp);
});
}
else
{
EmitVectorBinaryOpF32(context, (n, m) =>
{
OperandType type = n.Type;
if (type == OperandType.FP64)
{
return context.Call(typeof(SoftFloat64).GetMethod(name), n, m, one);
}
else
{
return context.Call(typeof(SoftFloat32).GetMethod(name), n, m, one);
}
return EmitSoftFloatCallDefaultFpscr(context, name, n, m);
});
}
}
@@ -357,11 +339,7 @@ namespace ARMeilleure.Instructions
me = ExtractScalar(context, type, op.Vm);
}
MethodInfo info = sizeF != 0
? typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))
: typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare));
Operand nzcv = context.Call(info, ne, me, Const(signalNaNs));
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
EmitSetFpscrNzcv(context, nzcv);
}

View File

@@ -76,7 +76,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0);
context.StoreToContext();
Operand res = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
context.LoadFromContext();
res = context.ZeroExtend16(OperandType.I64, res);
@@ -98,7 +100,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
context.StoreToContext();
Operand res = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
context.LoadFromContext();
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
}
@@ -120,7 +124,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = context.VectorExtract(OperandType.FP64, GetVec(op.Rn), 0);
context.StoreToContext();
Operand res = context.Call(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)), ne);
context.LoadFromContext();
res = context.ZeroExtend16(OperandType.I64, res);
@@ -143,7 +149,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
context.StoreToContext();
Operand res = context.Call(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)), ne);
context.LoadFromContext();
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
}
@@ -224,7 +232,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = EmitVectorExtractZx(context, op.Rn, part + index, 1);
context.StoreToContext();
Operand e = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
context.LoadFromContext();
res = context.VectorInsert(res, e, index);
}
@@ -333,7 +343,9 @@ namespace ARMeilleure.Instructions
if (sizeF == 0)
{
context.StoreToContext();
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
context.LoadFromContext();
e = context.ZeroExtend16(OperandType.I64, e);

View File

@@ -161,34 +161,15 @@ namespace ARMeilleure.Instructions
{
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
Operand asInteger;
// TODO: Fast Path.
if (roundWithFpscr)
{
MethodInfo info;
if (floatSize == OperandType.FP64)
{
info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32));
}
else
{
info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32));
}
asInteger = context.Call(info, toConvert);
}
else
{
// Round towards zero.
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
toConvert = EmitRoundByRMode(context, toConvert);
}
// Round towards zero.
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
InsertScalar(context, op.Vd, asInteger);
}
}
@@ -271,9 +252,7 @@ namespace ARMeilleure.Instructions
break;
}
Operand asInteger;
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
InsertScalar(context, op.Vd, asInteger);
}
@@ -399,15 +378,9 @@ namespace ARMeilleure.Instructions
// VRINTX (floating-point).
public static void Vrintx_S(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
bool doubleSize = (op.Size & 1) == 1;
string methodName = doubleSize ? nameof(SoftFallback.Round) : nameof(SoftFallback.RoundF);
EmitScalarUnaryOpF32(context, (op1) =>
{
MethodInfo info = typeof(SoftFallback).GetMethod(methodName);
return context.Call(info, op1);
return EmitRoundByRMode(context, op1);
});
}

View File

@@ -361,6 +361,54 @@ namespace ARMeilleure.Instructions
return context.Call(info, n, Const((int)roundMode));
}
public static Operand EmitGetRoundingMode(ArmEmitterContext context)
{
Operand rMode = context.ShiftLeft(GetFpFlag(FPState.RMode1Flag), Const(1));
rMode = context.BitwiseOr(rMode, GetFpFlag(FPState.RMode0Flag));
return rMode;
}
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.FP32 || op.Type == OperandType.FP64);
Operand lbl1 = Label();
Operand lbl2 = Label();
Operand lbl3 = Label();
Operand lblEnd = Label();
Operand rN = Const((int)FPRoundingMode.ToNearest);
Operand rP = Const((int)FPRoundingMode.TowardsPlusInfinity);
Operand rM = Const((int)FPRoundingMode.TowardsMinusInfinity);
Operand res = context.AllocateLocal(op.Type);
Operand rMode = EmitGetRoundingMode(context);
context.BranchIf(lbl1, rMode, rN, Comparison.NotEqual);
context.Copy(res, EmitRoundMathCall(context, MidpointRounding.ToEven, op));
context.Branch(lblEnd);
context.MarkLabel(lbl1);
context.BranchIf(lbl2, rMode, rP, Comparison.NotEqual);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Ceiling), op));
context.Branch(lblEnd);
context.MarkLabel(lbl2);
context.BranchIf(lbl3, rMode, rM, Comparison.NotEqual);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Floor), op));
context.Branch(lblEnd);
context.MarkLabel(lbl3);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Truncate), op));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
public static Operand EmitSoftFloatCall(ArmEmitterContext context, string name, params Operand[] callArgs)
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
@@ -369,7 +417,11 @@ namespace ARMeilleure.Instructions
? typeof(SoftFloat32).GetMethod(name)
: typeof(SoftFloat64).GetMethod(name);
return context.Call(info, callArgs);
context.StoreToContext();
Operand res = context.Call(info, callArgs);
context.LoadFromContext();
return res;
}
public static void EmitScalarBinaryOpByElemF(ArmEmitterContext context, Func2I emit)
@@ -1269,7 +1321,7 @@ namespace ARMeilleure.Instructions
public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue)
{
isTrue = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)));
isTrue = GetFpFlag(FPState.FzFlag);
Operand lblTrue = Label();
context.BranchIfFalse(lblTrue, isTrue);
@@ -1281,9 +1333,7 @@ namespace ARMeilleure.Instructions
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
{
isTrue = isTrue == default
? context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)))
: isTrue;
isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue;
Operand lblTrue = Label();
context.BranchIfFalse(lblTrue, isTrue);
@@ -1552,13 +1602,13 @@ namespace ARMeilleure.Instructions
context.BranchIf(lbl1, op, zeroL, Comparison.LessOrEqual);
context.Copy(res, maxT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lbl1);
context.BranchIf(lblEnd, op, zeroL, Comparison.GreaterOrEqual);
context.Copy(res, minT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1583,7 +1633,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblEnd, op, zeroUL, Comparison.LessOrEqualUI);
context.Copy(res, maxT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1610,13 +1660,13 @@ namespace ARMeilleure.Instructions
context.BranchIf(lbl1, op, maxT, Comparison.LessOrEqual);
context.Copy(res, maxT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lbl1);
context.BranchIf(lblEnd, op, minT, Comparison.GreaterOrEqual);
context.Copy(res, minT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1641,7 +1691,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblEnd, op, maxT, Comparison.LessOrEqualUI);
context.Copy(res, maxT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1663,7 +1713,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblEnd, op, minL, Comparison.NotEqual);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1691,7 +1741,7 @@ namespace ARMeilleure.Instructions
Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL);
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1713,7 +1763,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI);
context.Copy(res, maxUL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1741,7 +1791,7 @@ namespace ARMeilleure.Instructions
Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL);
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1763,7 +1813,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblEnd, op1, op2, Comparison.GreaterOrEqualUI);
context.Copy(res, zeroL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1790,19 +1840,19 @@ namespace ARMeilleure.Instructions
Operand notOp2AndRes = context.BitwiseAnd(context.BitwiseNot(op2), add);
context.BranchIf(lblEnd, notOp2AndRes, zeroL, Comparison.GreaterOrEqual);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lbl1);
context.BranchIf(lbl2, op2, zeroL, Comparison.Less);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lbl2);
context.BranchIf(lblEnd, add, maxL, Comparison.LessOrEqualUI);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
@@ -1828,14 +1878,14 @@ namespace ARMeilleure.Instructions
context.BranchIf(lbl1, op1, zeroL, Comparison.Less);
context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI);
context.Copy(res, maxUL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lbl1);
context.BranchIf(lblEnd, op2, maxL, Comparison.GreaterUI);
context.BranchIf(lblEnd, add, zeroL, Comparison.GreaterOrEqual);
context.Copy(res, zeroL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);

View File

@@ -1181,7 +1181,11 @@ namespace ARMeilleure.Instructions
Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1);
return context.Call(info, callArgs);
context.StoreToContext();
Operand res = context.Call(info, callArgs);
context.LoadFromContext();
return res;
}
public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size)

View File

@@ -1,5 +1,6 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
@@ -378,7 +379,7 @@ namespace ARMeilleure.Instructions
context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetFpFlag(context, FPState.QcFlag, Const(1));
context.MarkLabel(lblNoSat);

View File

@@ -31,8 +31,8 @@ namespace ARMeilleure.Instructions
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
@@ -53,9 +53,9 @@ namespace ARMeilleure.Instructions
switch (GetPackedId(op))
{
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break;
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break;
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
@@ -121,39 +121,91 @@ namespace ARMeilleure.Instructions
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
Operand nzcv = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
SetIntOrZR(context, op.Rt, nzcv);
}
SetIntOrZR(context, op.Rt, nzcvSh);
private static void EmitGetFpcr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpcr = Const(0);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
{
fpcr = context.BitwiseOr(fpcr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
SetIntOrZR(context, op.Rt, fpcr);
}
private static void EmitGetFpsr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpsr = Const(0);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
{
fpsr = context.BitwiseOr(fpsr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
SetIntOrZR(context, op.Rt, fpsr);
}
private static void EmitSetNzcv(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand t = GetIntOrZR(context, op.Rt);
t = context.ConvertI64ToI32(t);
Operand nzcv = GetIntOrZR(context, op.Rt);
nzcv = context.ConvertI64ToI32(nzcv);
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
v = context.BitwiseAnd (v, Const(1));
SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.VFlag)), Const(1)));
SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.CFlag)), Const(1)));
SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.ZFlag)), Const(1)));
SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.NFlag)), Const(1)));
}
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
c = context.BitwiseAnd (c, Const(1));
private static void EmitSetFpcr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
z = context.BitwiseAnd (z, Const(1));
Operand fpcr = GetIntOrZR(context, op.Rt);
fpcr = context.ConvertI64ToI32(fpcr);
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
n = context.BitwiseAnd (n, Const(1));
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
}
}
}
SetFlag(context, PState.VFlag, v);
SetFlag(context, PState.CFlag, c);
SetFlag(context, PState.ZFlag, z);
SetFlag(context, PState.NFlag, n);
private static void EmitSetFpsr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpsr = GetIntOrZR(context, op.Rt);
fpsr = context.ConvertI64ToI32(fpsr);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
}
}
}
}
}

View File

@@ -169,14 +169,11 @@ namespace ARMeilleure.Instructions
}
else
{
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
Operand qSh = context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag));
Operand spsr = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
spsr = context.BitwiseOr(spsr, qSh);
Operand spsr = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag)));
// TODO: Remaining flags.
@@ -200,8 +197,7 @@ namespace ARMeilleure.Instructions
EmitSetNzcv(context, value);
Operand q = context.ShiftRightUI(value, Const((int)PState.QFlag));
q = context.BitwiseAnd(q, Const(1));
Operand q = context.BitwiseAnd(context.ShiftRightUI(value, Const((int)PState.QFlag)), Const(1));
SetFlag(context, PState.QFlag, q);
}
@@ -284,17 +280,10 @@ namespace ARMeilleure.Instructions
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
{
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
v = context.BitwiseAnd(v, Const(1));
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
c = context.BitwiseAnd(c, Const(1));
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
z = context.BitwiseAnd(z, Const(1));
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
n = context.BitwiseAnd(n, Const(1));
Operand v = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.VFlag)), Const(1));
Operand c = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.CFlag)), Const(1));
Operand z = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.ZFlag)), Const(1));
Operand n = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.NFlag)), Const(1));
SetFlag(context, PState.VFlag, v);
SetFlag(context, PState.CFlag, c);
@@ -306,42 +295,32 @@ namespace ARMeilleure.Instructions
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Operand vSh = context.ShiftLeft(GetFpFlag(FPState.VFlag), Const((int)FPState.VFlag));
Operand cSh = context.ShiftLeft(GetFpFlag(FPState.CFlag), Const((int)FPState.CFlag));
Operand zSh = context.ShiftLeft(GetFpFlag(FPState.ZFlag), Const((int)FPState.ZFlag));
Operand nSh = context.ShiftLeft(GetFpFlag(FPState.NFlag), Const((int)FPState.NFlag));
Operand fpscr = Const(0);
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
{
fpscr = context.BitwiseOr(fpscr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
Operand fpscr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr)));
SetIntA32(context, op.Rt, context.BitwiseOr(nzcvSh, fpscr));
SetIntA32(context, op.Rt, fpscr);
}
private static void EmitSetFpscr(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Operand t = GetIntA32(context, op.Rt);
Operand fpscr = GetIntA32(context, op.Rt);
Operand v = context.ShiftRightUI(t, Const((int)FPState.VFlag));
v = context.BitwiseAnd(v, Const(1));
Operand c = context.ShiftRightUI(t, Const((int)FPState.CFlag));
c = context.BitwiseAnd(c, Const(1));
Operand z = context.ShiftRightUI(t, Const((int)FPState.ZFlag));
z = context.BitwiseAnd(z, Const(1));
Operand n = context.ShiftRightUI(t, Const((int)FPState.NFlag));
n = context.BitwiseAnd(n, Const(1));
SetFpFlag(context, FPState.VFlag, v);
SetFpFlag(context, FPState.CFlag, c);
SetFpFlag(context, FPState.ZFlag, z);
SetFpFlag(context, FPState.NFlag, n);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr)), t);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
}
}
}
}
}

View File

@@ -72,29 +72,6 @@ namespace ARMeilleure.Instructions
return (ulong)GetContext().DczidEl0;
}
public static ulong GetFpcr()
{
return (ulong)GetContext().Fpcr;
}
public static bool GetFpcrFz()
{
return (GetContext().Fpcr & FPCR.Fz) != 0;
}
public static ulong GetFpsr()
{
return (ulong)GetContext().Fpsr;
}
public static uint GetFpscr()
{
ExecutionContext context = GetContext();
return (uint)(context.Fpsr & FPSR.A32Mask & ~FPSR.Nzcv) |
(uint)(context.Fpcr & FPCR.A32Mask);
}
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
@@ -130,29 +107,6 @@ namespace ARMeilleure.Instructions
return GetContext().CntvctEl0;
}
public static void SetFpcr(ulong value)
{
GetContext().Fpcr = (FPCR)value;
}
public static void SetFpsr(ulong value)
{
GetContext().Fpsr = (FPSR)value;
}
public static void SetFpsrQc()
{
GetContext().Fpsr |= FPSR.Qc;
}
public static void SetFpscr(uint fpscr)
{
ExecutionContext context = GetContext();
context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
}
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;

View File

@@ -91,76 +91,6 @@ namespace ARMeilleure.Instructions
}
#endregion
#region "Rounding"
public static double Round(double value)
{
ExecutionContext context = NativeInterface.GetContext();
FPRoundingMode roundMode = context.Fpcr.GetRoundingMode();
if (roundMode == FPRoundingMode.ToNearest)
{
return Math.Round(value); // even
}
else if (roundMode == FPRoundingMode.TowardsPlusInfinity)
{
return Math.Ceiling(value);
}
else if (roundMode == FPRoundingMode.TowardsMinusInfinity)
{
return Math.Floor(value);
}
else /* if (roundMode == FPRoundingMode.TowardsZero) */
{
return Math.Truncate(value);
}
}
public static float RoundF(float value)
{
ExecutionContext context = NativeInterface.GetContext();
FPRoundingMode roundMode = context.Fpcr.GetRoundingMode();
if (roundMode == FPRoundingMode.ToNearest)
{
return MathF.Round(value); // even
}
else if (roundMode == FPRoundingMode.TowardsPlusInfinity)
{
return MathF.Ceiling(value);
}
else if (roundMode == FPRoundingMode.TowardsMinusInfinity)
{
return MathF.Floor(value);
}
else /* if (roundMode == FPRoundingMode.TowardsZero) */
{
return MathF.Truncate(value);
}
}
public static int FloatToInt32(float value)
{
return SatF32ToS32(RoundF(value));
}
public static int DoubleToInt32(double value)
{
return SatF64ToS32(Round(value));
}
public static uint FloatToUInt32(float value)
{
return SatF32ToU32(RoundF(value));
}
public static uint DoubleToUInt32(double value)
{
return SatF64ToU32(Round(value));
}
#endregion
#region "Saturation"
public static int SatF32ToS32(float value)
{

View File

@@ -12,8 +12,8 @@ namespace ARMeilleure.Instructions
RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable();
}
internal static readonly byte[] RecipEstimateTable;
internal static readonly byte[] RecipSqrtEstimateTable;
public static readonly byte[] RecipEstimateTable;
public static readonly byte[] RecipSqrtEstimateTable;
private static byte[] BuildRecipEstimateTable()
{
@@ -94,6 +94,13 @@ namespace ARMeilleure.Instructions
context.Fpsr |= (FPSR)(1 << (int)exc);
}
}
public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
{
const int RModeShift = 22;
return (FPRoundingMode)(((uint)fpcr >> RModeShift) & 3u);
}
}
static class SoftFloat16

View File

@@ -36,10 +36,25 @@ namespace ARMeilleure.State
set => _nativeContext.SetPstate(value);
}
public FPCR Fpcr { get; set; }
public FPSR Fpsr { get; set; }
public FPSR Fpsr
{
get => (FPSR)_nativeContext.GetFPState((uint)FPSR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPSR.Mask);
}
public FPCR Fpcr
{
get => (FPCR)_nativeContext.GetFPState((uint)FPCR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPCR.Mask);
}
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
public FPSCR Fpscr
{
get => (FPSCR)_nativeContext.GetFPState((uint)FPSCR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPSCR.Mask);
}
public bool IsAarch32 { get; set; }
internal ExecutionMode ExecutionMode

View File

@@ -5,21 +5,18 @@ namespace ARMeilleure.State
[Flags]
public enum FPCR : uint
{
Ioe = 1u << 8,
Dze = 1u << 9,
Ofe = 1u << 10,
Ufe = 1u << 11,
Ixe = 1u << 12,
Ide = 1u << 15,
RMode0 = 1u << 22,
RMode1 = 1u << 23,
Fz = 1u << 24,
Dn = 1u << 25,
Ahp = 1u << 26,
A32Mask = 0x07FF9F00u
}
public static class FPCRExtensions
{
private const int RModeShift = 22;
public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
{
return (FPRoundingMode)(((int)fpcr >> RModeShift) & 3);
}
Mask = Ahp | Dn | Fz | RMode1 | RMode0 | Ide | Ixe | Ufe | Ofe | Dze | Ioe // 0x07C09F00u
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace ARMeilleure.State
{
[Flags]
public enum FPSCR : uint
{
V = 1u << 28,
C = 1u << 29,
Z = 1u << 30,
N = 1u << 31,
Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask // 0xFFC09F9Fu
}
}

View File

@@ -5,11 +5,14 @@ namespace ARMeilleure.State
[Flags]
public enum FPSR : uint
{
Ioc = 1u << 0,
Dzc = 1u << 1,
Ofc = 1u << 2,
Ufc = 1u << 3,
Qc = 1u << 27,
Ixc = 1u << 4,
Idc = 1u << 7,
Qc = 1u << 27,
Nzcv = (1u << 31) | (1u << 30) | (1u << 29) | (1u << 28),
A32Mask = 0xF800009Fu
Mask = Qc | Idc | Ixc | Ufc | Ofc | Dzc | Ioc // 0x0800009Fu
}
}

View File

@@ -2,9 +2,30 @@
{
public enum FPState
{
// FPSR Flags.
IocFlag = 0,
DzcFlag = 1,
OfcFlag = 2,
UfcFlag = 3,
IxcFlag = 4,
IdcFlag = 7,
QcFlag = 27,
VFlag = 28,
CFlag = 29,
ZFlag = 30,
NFlag = 31
NFlag = 31,
// FPCR Flags.
IoeFlag = 8,
DzeFlag = 9,
OfeFlag = 10,
UfeFlag = 11,
IxeFlag = 12,
IdeFlag = 15,
RMode0Flag = 22,
RMode1Flag = 23,
FzFlag = 24,
DnFlag = 25,
AhpFlag = 26
}
}

View File

@@ -140,6 +140,34 @@ namespace ARMeilleure.State
GetStorage().FpFlags[(int)flag] = value ? 1u : 0u;
}
public unsafe uint GetFPState(uint mask = uint.MaxValue)
{
uint value = 0;
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
uint bit = 1u << flag;
if ((mask & bit) == bit)
{
value |= GetStorage().FpFlags[flag] != 0 ? bit : 0u;
}
}
return value;
}
public unsafe void SetFPState(uint value, uint mask = uint.MaxValue)
{
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
uint bit = 1u << flag;
if ((mask & bit) == bit)
{
GetStorage().FpFlags[flag] = (value & bit) == bit ? 1u : 0u;
}
}
}
public int GetCounter() => GetStorage().Counter;
public void SetCounter(int value) => GetStorage().Counter = value;

View File

@@ -109,10 +109,6 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
@@ -124,10 +120,6 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
@@ -151,12 +143,8 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32))); // A32 only.
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))); // A32 only.
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32))); // A32 only.
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))); // A32 only.
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
@@ -165,8 +153,6 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));

View File

@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 3700; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 3703; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
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 SharedDataFileName = "shared.data";

View File

@@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@@ -163,9 +164,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
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 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();
@@ -185,14 +194,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
if (context.Config.UsedInputAttributesPerPatch != 0)
if (context.Config.UsedInputAttributesPerPatch.Count != 0)
{
DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch);
context.AppendLine();
}
if (context.Config.UsedOutputAttributesPerPatch != 0)
if (context.Config.UsedOutputAttributesPerPatch.Count != 0)
{
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, index);
usedAttributes &= ~(1 << index);
DeclareInputAttributePerPatch(context, attr);
}
}
@@ -566,16 +573,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr)
{
string layout = string.Empty;
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
layout = $"layout (location = {32 + attr}) ";
}
int location = context.Config.GetPerPatchAttributeLocation(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)
@@ -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, index);
usedAttributes &= ~(1 << index);
DeclareOutputAttributePerPatch(context, attr);
}
}
private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr)
{
string layout = string.Empty;
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
layout = $"layout (location = {32 + attr}) ";
}
int location = context.Config.GetPerPatchAttributeLocation(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)

View File

@@ -28,33 +28,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
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.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) },
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) },
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) },
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) },
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) },
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) },
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) },
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) },
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) },
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) },
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) },
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) },
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) },
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) },
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) },
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) },
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) },
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) },
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) },
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) },
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) },
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) },
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) },
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) },
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) },
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) },
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) },
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) },
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
// Special.
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) },
@@ -170,7 +164,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
value &= AttributeConsts.Mask & ~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;
@@ -180,11 +196,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
if (!indexable && perPatch)
{
prefix = DefaultNames.PerPatchAttributePrefix;
}
if (indexable)
{
string name = prefix;
@@ -202,7 +213,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
string name = $"{prefix}{(value >> 4)}_{swzMask}";
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
}
@@ -213,7 +224,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
string name = $"{prefix}{(value >> 4)}";
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
}
@@ -277,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
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}";
}

View File

@@ -382,17 +382,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
{
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;
Instruction ioVariable;
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
{
return ioVariable;
@@ -404,7 +400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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));
}

View File

@@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
foreach (int attr in inputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false))
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
{
continue;
}
@@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
foreach (int attr in outputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true))
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
{
continue;
}
@@ -519,7 +519,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
: (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))
{
@@ -544,11 +546,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass);
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
if (builtInPassthrough)
{
context.Decorate(spvVar, Decoration.PassthroughNV);
@@ -556,6 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (attrInfo.IsBuiltin)
{
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
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)
{
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)
{
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
{

View File

@@ -191,7 +191,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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);
}
@@ -375,9 +383,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
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;
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch))
{
bool perPatch = dest.Type == OperandType.AttributePerPatch;
AggregateType elemType;
var elemPointer = perPatch

View File

@@ -306,18 +306,36 @@ namespace Ryujinx.Graphics.Shader.Decoders
for (int elemIndex = 0; elemIndex < count; elemIndex++)
{
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 index = userAttr / 16;
if (isStore)
{
config.SetOutputUserAttribute(index, perPatch);
config.SetOutputUserAttribute(index);
}
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);
if (op.O)
if (op.O && CanLoadOutput(offset))
{
offset |= AttributeConsts.LoadOutputMask;
}
@@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(offset);
if (op.O)
if (op.O && CanLoadOutput(offset))
{
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)
{
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR)

View File

@@ -97,7 +97,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
{
int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
context.Info.Inputs.Add(attrOffset);
if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
{
context.Info.Outputs.Add(attrOffset);
}
else
{
context.Info.Inputs.Add(attrOffset);
}
}
}

View File

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

View File

@@ -4,36 +4,30 @@ namespace Ryujinx.Graphics.Shader.Translation
{
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.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 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.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 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.Layer, new AttributeInfo(AttributeConsts.Layer, 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.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 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.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, 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.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
// Special.
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
@@ -55,6 +49,16 @@ namespace Ryujinx.Graphics.Shader.Translation
{ 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 Value { get; }
public int Length { get; }
@@ -76,6 +80,11 @@ namespace Ryujinx.Graphics.Shader.Translation
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)
{
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
@@ -86,6 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation
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)
{
value &= ~3;
@@ -115,7 +129,24 @@ namespace Ryujinx.Graphics.Shader.Translation
{
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;
}

View File

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

View File

@@ -50,16 +50,16 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool NextUsesFixedFuncAttributes { get; private set; }
public int UsedInputAttributes { get; private set; }
public int UsedOutputAttributes { get; private set; }
public int UsedInputAttributesPerPatch { get; private set; }
public int UsedOutputAttributesPerPatch { get; private set; }
public HashSet<int> UsedInputAttributesPerPatch { get; }
public HashSet<int> UsedOutputAttributesPerPatch { get; }
public HashSet<int> NextUsedInputAttributesPerPatch { get; private set; }
public int PassthroughAttributes { get; private set; }
private int _nextUsedInputAttributes;
private int _thisUsedInputAttributes;
private Dictionary<int, int> _perPatchAttributeLocations;
public UInt128 NextInputAttributesComponents { 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 _usedStorageBuffers;
@@ -119,9 +119,13 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options)
{
Stage = ShaderStage.Compute;
GpuAccessor = gpuAccessor;
Options = options;
Stage = ShaderStage.Compute;
GpuAccessor = gpuAccessor;
Options = options;
UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>();
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
}
@@ -244,49 +248,71 @@ namespace Ryujinx.Graphics.Shader.Translation
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;
UsedInputAttributes |= mask;
_thisUsedInputAttributes |= mask;
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component);
}
UsedInputAttributes |= mask;
_thisUsedInputAttributes |= mask;
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component);
}
public void SetOutputUserAttribute(int index, bool perPatch)
public void SetInputUserAttributePerPatch(int index)
{
if (perPatch)
{
UsedOutputAttributesPerPatch |= 1 << index;
}
else
{
UsedOutputAttributes |= 1 << index;
}
UsedInputAttributesPerPatch.Add(index);
}
public void SetOutputUserAttribute(int index)
{
UsedOutputAttributes |= 1 << index;
}
public void SetOutputUserAttributePerPatch(int index)
{
UsedOutputAttributesPerPatch.Add(index);
}
public void MergeFromtNextStage(ShaderConfig config)
{
NextInputAttributesComponents = config.ThisInputAttributesComponents;
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch;
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
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)
{
LastInVertexPipeline = false;
}
}
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
public void MergeOutputUserAttributes(int mask, IEnumerable<int> perPatch)
{
_nextUsedInputAttributes = mask;
@@ -297,10 +323,20 @@ namespace Ryujinx.Graphics.Shader.Translation
else
{
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)
{
// 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);
}
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;
while (usedAttributesPerPatch != UInt128.Zero)
if (context.Config.NextUsedInputAttributesPerPatch != null)
{
int index = usedAttributesPerPatch.TrailingZeroCount();
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true);
usedAttributesPerPatch &= ~UInt128.Pow2(index);
foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x))
{
InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true);
}
}
if (config.NextUsesFixedFuncAttributes)
@@ -236,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int c = 0; c < 4; c++)
{
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.IntermediateRepresentation;
using System.Collections.Generic;
using System.Linq;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
using static Ryujinx.Graphics.Shader.Translation.Translator;
@@ -137,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation
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);

View File

@@ -370,6 +370,7 @@ namespace Ryujinx.Graphics.Vulkan
{
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
_cachedConvertedBuffers.Add(offset, size, key, holder);
@@ -388,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
key.SetBuffer(holder.GetBuffer());
@@ -398,6 +400,29 @@ namespace Ryujinx.Graphics.Vulkan
return holder.GetBuffer();
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
// The destination index size is always I32.
int indexCount = size / indexSize;
int convertedCount = pattern.GetConvertedCount(indexCount);
holder = _gd.BufferManager.Create(_gd, convertedCount * 4);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public void Dispose()
{
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);

View File

@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out var holder))

View File

@@ -1,5 +1,4 @@
using Silk.NET.Vulkan;
using System;
using System;
namespace Ryujinx.Graphics.Vulkan
{
@@ -9,38 +8,17 @@ namespace Ryujinx.Graphics.Vulkan
private readonly int _offset;
private readonly int _size;
private readonly IndexType _type;
private readonly Auto<DisposableBuffer> _buffer;
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
{
_buffer = buffer;
_offset = offset;
_size = size;
_type = type;
buffer?.IncrementReferenceCount();
}
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{
_buffer = buffer;
_offset = offset;
_size = size;
_type = IndexType.Uint16;
buffer?.IncrementReferenceCount();
}
public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
{
if (_buffer != null)
{
api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
}
}
public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
{
if (_buffer != null)

View File

@@ -10,14 +10,25 @@ namespace Ryujinx.Graphics.Vulkan
struct I8ToI16CacheKey : ICacheKey
{
public I8ToI16CacheKey() { }
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public I8ToI16CacheKey(VulkanRenderer gd)
{
_gd = gd;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is I8ToI16CacheKey;
}
public void Dispose() { }
public void Dispose()
{
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
}
}
struct AlignedVertexBufferCacheKey : ICacheKey
@@ -55,6 +66,41 @@ namespace Ryujinx.Graphics.Vulkan
}
}
struct TopologyConversionCacheKey : ICacheKey
{
private IndexBufferPattern _pattern;
private int _indexSize;
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
{
_gd = gd;
_pattern = pattern;
_indexSize = indexSize;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is TopologyConversionCacheKey entry &&
entry._pattern == _pattern &&
entry._indexSize == _indexSize;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
}
}
struct CacheByRange<T> where T : IDisposable
{
private struct Entry

View File

@@ -2,6 +2,7 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan
{
@@ -179,8 +180,8 @@ namespace Ryujinx.Graphics.Vulkan
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
GAL.PrimitiveTopology.Quads => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, // Emulated with triangle fans
GAL.PrimitiveTopology.QuadStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, // Emulated with triangle strips
GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
};
}

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using Ryujinx.Graphics.Vulkan.Shaders;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan
@@ -399,6 +400,86 @@ namespace Ryujinx.Graphics.Vulkan
newSize);
}
public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
CommandBufferScoped cbs,
BufferHolder src,
BufferHolder dst,
IndexBufferPattern pattern,
int indexSize,
int srcOffset,
int indexCount)
{
int convertedCount = pattern.GetConvertedCount(indexCount);
int outputIndexSize = 4;
// TODO: Do this with a compute shader?
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
var bufferCopy = new List<BufferCopy>();
int outputOffset = 0;
// Try to merge copies of adjacent indices to reduce copy count.
int sequenceStart = 0;
int sequenceLength = 0;
foreach (var index in pattern.GetIndexMapping(indexCount))
{
if (sequenceLength > 0)
{
if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
{
sequenceLength++;
continue;
}
// Commit the copy so far.
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
outputOffset += outputIndexSize * sequenceLength;
}
sequenceStart = index;
sequenceLength = 1;
}
if (sequenceLength > 0)
{
// Commit final pending copy.
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
}
var bufferCopyArray = bufferCopy.ToArray();
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.AccessTransferWriteBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageTransferBit,
0,
convertedCount * outputIndexSize);
fixed (BufferCopy* pBufferCopy = bufferCopyArray)
{
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
}
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
AccessFlags.AccessTransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.PipelineStageTransferBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
0,
convertedCount * outputIndexSize);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)

View File

@@ -0,0 +1,139 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
internal class IndexBufferPattern : IDisposable
{
public int PrimitiveVertices { get; }
public int PrimitiveVerticesOut { get; }
public int BaseIndex { get; }
public int[] OffsetIndex { get; }
public int IndexStride { get; }
public bool RepeatStart { get; }
private VulkanRenderer _gd;
private int _currentSize;
private BufferHandle _repeatingBuffer;
public IndexBufferPattern(VulkanRenderer gd,
int primitiveVertices,
int primitiveVerticesOut,
int baseIndex,
int[] offsetIndex,
int indexStride,
bool repeatStart)
{
PrimitiveVertices = primitiveVertices;
PrimitiveVerticesOut = primitiveVerticesOut;
BaseIndex = baseIndex;
OffsetIndex = offsetIndex;
IndexStride = indexStride;
RepeatStart = repeatStart;
_gd = gd;
}
public int GetPrimitiveCount(int vertexCount)
{
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
}
public int GetConvertedCount(int indexCount)
{
int primitiveCount = GetPrimitiveCount(indexCount);
return primitiveCount * OffsetIndex.Length;
}
public IEnumerable<int> GetIndexMapping(int indexCount)
{
int primitiveCount = GetPrimitiveCount(indexCount);
int index = BaseIndex;
for (int i = 0; i < primitiveCount; i++)
{
if (RepeatStart)
{
// Used for triangle fan
yield return 0;
}
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
{
yield return index + OffsetIndex[j];
}
index += IndexStride;
}
}
public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
{
int primitiveCount = GetPrimitiveCount(vertexCount);
indexCount = primitiveCount * PrimitiveVerticesOut;
int expectedSize = primitiveCount * OffsetIndex.Length;
if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
{
return _repeatingBuffer;
}
// Expand the repeating pattern to the number of requested primitives.
BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
// Copy the old data to the new one.
if (_repeatingBuffer != BufferHandle.Null)
{
_gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
_gd.DeleteBuffer(_repeatingBuffer);
}
_repeatingBuffer = newBuffer;
// Add the additional repeats on top.
int newPrimitives = primitiveCount;
int oldPrimitives = (_currentSize) / OffsetIndex.Length;
int[] newData;
newPrimitives -= oldPrimitives;
newData = new int[expectedSize - _currentSize];
int outOffset = 0;
int index = oldPrimitives * IndexStride + BaseIndex;
for (int i = 0; i < newPrimitives; i++)
{
if (RepeatStart)
{
// Used for triangle fan
newData[outOffset++] = 0;
}
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
{
newData[outOffset++] = index + OffsetIndex[j];
}
index += IndexStride;
}
_gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
_currentSize = expectedSize;
return newBuffer;
}
public void Dispose()
{
if (_repeatingBuffer != BufferHandle.Null)
{
_gd.DeleteBuffer(_repeatingBuffer);
_repeatingBuffer = BufferHandle.Null;
}
}
}
}

View File

@@ -0,0 +1,97 @@
using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan
{
internal struct IndexBufferState
{
public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0);
private readonly int _offset;
private readonly int _size;
private readonly IndexType _type;
private readonly GAL.BufferHandle _handle;
private Auto<DisposableBuffer> _buffer;
public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type)
{
_handle = handle;
_offset = offset;
_size = size;
_type = type;
_buffer = null;
}
public IndexBufferState(GAL.BufferHandle handle, int offset, int size)
{
_handle = handle;
_offset = offset;
_size = size;
_type = IndexType.Uint16;
_buffer = null;
}
public void BindIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs)
{
Auto<DisposableBuffer> autoBuffer;
int offset, size;
IndexType type = _type;
if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8)
{
// Index type is not supported. Convert to I16.
autoBuffer = gd.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
type = IndexType.Uint16;
offset = 0;
size = _size * 2;
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
offset = _offset;
size = _size;
}
_buffer = autoBuffer;
if (autoBuffer != null)
{
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type);
}
}
public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
{
Auto<DisposableBuffer> autoBuffer;
// Convert the index buffer using the given pattern.
int indexSize = _type switch
{
IndexType.Uint32 => 4,
IndexType.Uint16 => 2,
_ => 1,
};
int firstIndexOffset = firstIndex * indexSize;
autoBuffer = gd.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
int size = convertedCount * 4;
_buffer = autoBuffer;
if (autoBuffer != null)
{
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
}
}
public bool BoundEquals(Auto<DisposableBuffer> buffer)
{
return _buffer == buffer;
}
}
}

View File

@@ -51,13 +51,16 @@ namespace Ryujinx.Graphics.Vulkan
private readonly DescriptorSetUpdater _descriptorSetUpdater;
private BufferState _indexBuffer;
private IndexBufferState _indexBuffer;
private IndexBufferPattern _indexBufferPattern;
private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor;
public SupportBufferUpdater SupportBufferUpdater;
public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern;
private bool _needsIndexBufferRebind;
private bool _needsTransformFeedbackBuffersRebind;
@@ -65,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled;
private bool _tfActive;
private PipelineColorBlendAttachmentState[] _storedBlend;
public ulong DrawCount { get; private set; }
public unsafe PipelineBase(VulkanRenderer gd, Device device)
@@ -101,12 +106,17 @@ namespace Ryujinx.Graphics.Vulkan
_newState.Initialize();
_newState.LineWidth = 1f;
_newState.SamplesCount = 1;
_storedBlend = new PipelineColorBlendAttachmentState[8];
}
public void Initialize()
{
SupportBufferUpdater = new SupportBufferUpdater(Gd);
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
}
public unsafe void Barrier()
@@ -245,6 +255,14 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer)
{
if (_indexBuffer.BoundEquals(buffer))
{
_needsIndexBufferRebind = true;
}
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
if (!_program.IsLinked)
@@ -267,24 +285,59 @@ namespace Ryujinx.Graphics.Vulkan
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads)
if (Gd.TopologyUnsupported(_topology))
{
int quadsCount = vertexCount / 4;
// Temporarily bind a conversion pattern as an index buffer.
_needsIndexBufferRebind = true;
for (int i = 0; i < quadsCount; i++)
IndexBufferPattern pattern = _topology switch
{
Gd.Api.CmdDraw(CommandBuffer, 4, (uint)instanceCount, (uint)(firstVertex + i * 4), (uint)firstInstance);
}
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false);
Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32);
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
}
else
{
ResumeTransformFeedbackInternal();
Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance);
}
}
private void UpdateIndexBufferPattern()
{
IndexBufferPattern pattern = null;
if (Gd.TopologyUnsupported(_topology))
{
pattern = _topology switch
{
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
}
if (_indexBufferPattern != pattern)
{
_indexBufferPattern = pattern;
_needsIndexBufferRebind = true;
}
}
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{
if (!_program.IsLinked)
@@ -292,22 +345,34 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads)
if (_indexBufferPattern != null)
{
int quadsCount = indexCount / 4;
// Convert the index buffer into a supported topology.
IndexBufferPattern pattern = _indexBufferPattern;
for (int i = 0; i < quadsCount; i++)
int convertedCount = pattern.GetConvertedCount(indexCount);
if (_needsIndexBufferRebind)
{
Gd.Api.CmdDrawIndexed(CommandBuffer, 4, (uint)instanceCount, (uint)(firstIndex + i * 4), firstVertex, (uint)firstInstance);
_indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern);
_needsIndexBufferRebind = false;
}
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
}
else
{
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance);
}
}
@@ -437,13 +502,28 @@ namespace Ryujinx.Graphics.Vulkan
{
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index];
vkBlend.BlendEnable = blend.Enable;
vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert();
vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert();
vkBlend.ColorBlendOp = blend.ColorOp.Convert();
vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert();
vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert();
vkBlend.AlphaBlendOp = blend.AlphaOp.Convert();
if (blend.Enable)
{
vkBlend.BlendEnable = blend.Enable;
vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert();
vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert();
vkBlend.ColorBlendOp = blend.ColorOp.Convert();
vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert();
vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert();
vkBlend.AlphaBlendOp = blend.AlphaOp.Convert();
}
else
{
vkBlend = new PipelineColorBlendAttachmentState(
colorWriteMask: vkBlend.ColorWriteMask);
}
if (vkBlend.ColorWriteMask == 0)
{
_storedBlend[index] = vkBlend;
vkBlend = new PipelineColorBlendAttachmentState();
}
_newState.BlendConstantR = blend.BlendConstant.Red;
_newState.BlendConstantG = blend.BlendConstant.Green;
@@ -500,34 +580,16 @@ namespace Ryujinx.Graphics.Vulkan
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
{
_indexBuffer.Dispose();
if (buffer.Handle != BufferHandle.Null)
{
Auto<DisposableBuffer> ib = null;
int offset = buffer.Offset;
int size = buffer.Size;
if (type == GAL.IndexType.UByte && !Gd.Capabilities.SupportsIndexTypeUint8)
{
ib = Gd.BufferManager.GetBufferI8ToI16(Cbs, buffer.Handle, offset, size);
offset = 0;
size *= 2;
type = GAL.IndexType.UShort;
}
else
{
ib = Gd.BufferManager.GetBuffer(CommandBuffer, buffer.Handle, false);
}
_indexBuffer = new BufferState(ib, offset, size, type.Convert());
_indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert());
}
else
{
_indexBuffer = BufferState.Null;
_indexBuffer = IndexBufferState.Null;
}
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
_needsIndexBufferRebind = true;
}
public void SetLineParameters(float width, bool smooth)
@@ -584,7 +646,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_topology = topology;
var vkTopology = topology.Convert();
var vkTopology = Gd.TopologyRemap(topology).Convert();
_newState.Topology = vkTopology;
@@ -626,8 +688,25 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i];
var newMask = (ColorComponentFlags)componentMask[i];
vkBlend.ColorWriteMask = (ColorComponentFlags)componentMask[i];
// When color write mask is 0, remove all blend state to help the pipeline cache.
// Restore it when the mask becomes non-zero.
if (vkBlend.ColorWriteMask != newMask)
{
if (newMask == 0)
{
_storedBlend[i] = vkBlend;
vkBlend = new PipelineColorBlendAttachmentState();
}
else if (vkBlend.ColorWriteMask == 0)
{
vkBlend = _storedBlend[i];
}
}
vkBlend.ColorWriteMask = newMask;
if (componentMask[i] != 0)
{
@@ -1127,9 +1206,9 @@ namespace Ryujinx.Graphics.Vulkan
// Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit();
if (_needsIndexBufferRebind)
if (_needsIndexBufferRebind && _indexBufferPattern == null)
{
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
_indexBuffer.BindIndexBuffer(Gd, Cbs);
_needsIndexBufferRebind = false;
}
@@ -1265,7 +1344,6 @@ namespace Ryujinx.Graphics.Vulkan
{
_renderPass?.Dispose();
_framebuffer?.Dispose();
_indexBuffer.Dispose();
_newState.Dispose();
_descriptorSetUpdater.Dispose();

View File

@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.Topology = state.Topology.Convert();
pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
@@ -257,15 +257,23 @@ namespace Ryujinx.Graphics.Vulkan
{
var blend = state.BlendDescriptors[i];
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
blend.Enable,
blend.ColorSrcFactor.Convert(),
blend.ColorDstFactor.Convert(),
blend.ColorOp.Convert(),
blend.AlphaSrcFactor.Convert(),
blend.AlphaDstFactor.Convert(),
blend.AlphaOp.Convert(),
(ColorComponentFlags)state.ColorWriteMask[i]);
if (blend.Enable && state.ColorWriteMask[i] != 0)
{
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
blend.Enable,
blend.ColorSrcFactor.Convert(),
blend.ColorDstFactor.Convert(),
blend.ColorOp.Convert(),
blend.AlphaSrcFactor.Convert(),
blend.AlphaDstFactor.Convert(),
blend.AlphaOp.Convert(),
(ColorComponentFlags)state.ColorWriteMask[i]);
}
else
{
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]);
}
}
int maxAttachmentIndex = 0;

View File

@@ -471,6 +471,25 @@ namespace Ryujinx.Graphics.Vulkan
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
}
public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
{
return topology switch
{
GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
_ => topology
};
}
public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
{
return topology switch
{
GAL.PrimitiveTopology.Quads => true,
_ => false
};
}
public void Initialize(GraphicsDebugLevel logLevel)
{
SetupContext(logLevel);

View File

@@ -22,7 +22,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
// outside of the AccountManager.
private readonly HorizonClient _horizonClient;
private ConcurrentDictionary<string, UserProfile> _profiles;
private readonly ConcurrentDictionary<string, UserProfile> _profiles;
private UserProfile[] _storedOpenedUsers;
public UserProfile LastOpenedUser { get; private set; }
@@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
_horizonClient = horizonClient;
_profiles = new ConcurrentDictionary<string, UserProfile>();
_storedOpenedUsers = Array.Empty<UserProfile>();
_accountSaveDataManager = new AccountSaveDataManager(_profiles);
@@ -44,9 +46,9 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
}
else
{
UserId commandLineUserProfileOverride = default;
UserId commandLineUserProfileOverride = default;
if (!string.IsNullOrEmpty(initialProfileName))
{
{
commandLineUserProfileOverride = _profiles.Values.FirstOrDefault(x => x.Name == initialProfileName)?.UserId ?? default;
if (commandLineUserProfileOverride.IsNull)
Logger.Warning?.Print(LogClass.Application, $"The command line specified profile named '{initialProfileName}' was not found");
@@ -221,6 +223,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return _profiles.Values.Where(x => x.AccountState == AccountState.Open);
}
internal IEnumerable<UserProfile> GetStoredOpenedUsers()
{
return _storedOpenedUsers;
}
internal void StoreOpenedUsers()
{
_storedOpenedUsers = _profiles.Values.Where(x => x.AccountState == AccountState.Open).ToArray();
}
internal UserProfile GetFirst()
{
return _profiles.First().Value;

View File

@@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode StoreOpenContext(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
context.Device.System.AccountManager.StoreOpenedUsers();
return ResultCode.Success;
}

View File

@@ -201,6 +201,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return ResultCode.Success;
}
public ResultCode ListOpenContextStoredUsers(ServiceCtx context)
{
return WriteUserList(context, context.Device.System.AccountManager.GetStoredOpenedUsers());
}
public ResultCode ListQualifiedUsers(ServiceCtx context)
{
// TODO: Determine how users are "qualified". We assume all users are "qualified" for now.

View File

@@ -1,5 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService;
using Ryujinx.HLE.HOS.Services.Arp;
@@ -139,20 +138,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return _applicationServiceServer.ClearSaveDataThumbnail(context);
}
[CommandHipc(130)] // 5.0.0+
// LoadOpenContext(nn::account::Uid)
public ResultCode LoadOpenContext(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
return ResultCode.Success;
}
[CommandHipc(60)] // 5.0.0-5.1.0
[CommandHipc(131)] // 6.0.0+
// ListOpenContextStoredUsers() -> array<nn::account::Uid, 0xa>
public ResultCode ListOpenContextStoredUsers(ServiceCtx context)
{
ulong outputPosition = context.Request.RecvListBuff[0].Position;
ulong outputSize = context.Request.RecvListBuff[0].Size;
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
// TODO: This seems to write stored userids of the OpenContext in the buffer. We needs to determine them.
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
return ResultCode.Success;
return _applicationServiceServer.ListOpenContextStoredUsers(context);
}
[CommandHipc(141)] // 6.0.0+

View File

@@ -181,7 +181,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
}
}
public bool TryGetCertificates(ReadOnlySpan<CaCertificateId> ids, out CertStoreEntry[] entries)
public bool TryGetCertificates(
ReadOnlySpan<CaCertificateId> ids,
out CertStoreEntry[] entries,
out bool hasAllCertificates,
out int requiredSize)
{
lock (_lock)
{
@@ -190,7 +194,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
throw new InvalidSystemResourceException(CertStoreTitleMissingErrorMessage);
}
bool hasAllCertificates = false;
requiredSize = 0;
hasAllCertificates = false;
foreach (CaCertificateId id in ids)
{
@@ -205,12 +210,14 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
if (hasAllCertificates)
{
entries = new CertStoreEntry[_certificates.Count];
requiredSize = (_certificates.Count + 1) * Unsafe.SizeOf<BuiltInCertificateInfo>();
int i = 0;
foreach (CertStoreEntry entry in _certificates.Values)
{
entries[i++] = entry;
requiredSize += (entry.Data.Length + 3) & ~3;
}
return true;
@@ -218,6 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
else
{
entries = new CertStoreEntry[ids.Length];
requiredSize = ids.Length * Unsafe.SizeOf<BuiltInCertificateInfo>();
for (int i = 0; i < ids.Length; i++)
{
@@ -227,6 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
}
entries[i] = entry;
requiredSize += (entry.Data.Length + 3) & ~3;
}
return true;

View File

@@ -29,42 +29,40 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
return ResultCode.Success;
}
private uint ComputeCertificateBufferSizeRequired(ReadOnlySpan<BuiltInCertificateManager.CertStoreEntry> entries)
{
uint totalSize = 0;
for (int i = 0; i < entries.Length; i++)
{
totalSize += (uint)Unsafe.SizeOf<BuiltInCertificateInfo>();
totalSize += (uint)entries[i].Data.Length;
}
return totalSize;
}
[CommandHipc(2)]
// GetCertificates(buffer<CaCertificateId, 5> ids) -> (u32 certificates_count, buffer<bytes, 6> certificates)
public ResultCode GetCertificates(ServiceCtx context)
{
ReadOnlySpan<CaCertificateId> ids = MemoryMarshal.Cast<byte, CaCertificateId>(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size));
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries))
if (!BuiltInCertificateManager.Instance.TryGetCertificates(
ids,
out BuiltInCertificateManager.CertStoreEntry[] entries,
out bool hasAllCertificates,
out int requiredSize))
{
throw new InvalidOperationException();
}
if (ComputeCertificateBufferSizeRequired(entries) > context.Request.ReceiveBuff[0].Size)
if ((uint)requiredSize > (uint)context.Request.ReceiveBuff[0].Size)
{
return ResultCode.InvalidCertBufSize;
}
int infosCount = entries.Length;
if (hasAllCertificates)
{
infosCount++;
}
using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size))
{
Span<byte> rawData = region.Memory.Span;
Span<BuiltInCertificateInfo> infos = MemoryMarshal.Cast<byte, BuiltInCertificateInfo>(rawData)[..entries.Length];
Span<byte> certificatesData = rawData[(Unsafe.SizeOf<BuiltInCertificateInfo>() * entries.Length)..];
Span<BuiltInCertificateInfo> infos = MemoryMarshal.Cast<byte, BuiltInCertificateInfo>(rawData)[..infosCount];
Span<byte> certificatesData = rawData[(Unsafe.SizeOf<BuiltInCertificateInfo>() * infosCount)..];
for (int i = 0; i < infos.Length; i++)
for (int i = 0; i < entries.Length; i++)
{
entries[i].Data.CopyTo(certificatesData);
@@ -78,6 +76,17 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
certificatesData = certificatesData[entries[i].Data.Length..];
}
if (hasAllCertificates)
{
infos[entries.Length] = new BuiltInCertificateInfo
{
Id = CaCertificateId.All,
Status = TrustedCertStatus.Invalid,
CertificateDataSize = 0,
CertificateDataOffset = 0
};
}
}
context.ResponseData.Write(entries.Length);
@@ -91,12 +100,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
{
ReadOnlySpan<CaCertificateId> ids = MemoryMarshal.Cast<byte, CaCertificateId>(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size));
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries))
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out _, out _, out int requiredSize))
{
throw new InvalidOperationException();
}
context.ResponseData.Write(ComputeCertificateBufferSizeRequired(entries));
context.ResponseData.Write(requiredSize);
return ResultCode.Success;
}

View File

@@ -162,7 +162,7 @@ namespace Ryujinx.Tests.Cpu
_context.SetPstateFlag(PState.ZFlag, zero);
_context.SetPstateFlag(PState.NFlag, negative);
SetFpscr((uint)fpscr);
_context.Fpscr = (FPSCR)fpscr;
_context.SetPstateFlag(PState.TFlag, thumb);
@@ -467,7 +467,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag), "NFlag");
});
Assert.That((int)GetFpscr() & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpscr & (int)fpsrMask), "Fpscr");
Assert.That((int)_context.Fpscr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpscr & (int)fpsrMask), "Fpscr");
if (_usingMemory)
{
@@ -650,28 +650,5 @@ namespace Ryujinx.Tests.Cpu
return rnd & 0x800FFFFFFFFFFFFFul;
}
private uint GetFpscr()
{
uint fpscr = (uint)(_context.Fpsr & FPSR.A32Mask & ~FPSR.Nzcv) | (uint)(_context.Fpcr & FPCR.A32Mask);
fpscr |= _context.GetFPstateFlag(FPState.NFlag) ? (1u << (int)FPState.NFlag) : 0;
fpscr |= _context.GetFPstateFlag(FPState.ZFlag) ? (1u << (int)FPState.ZFlag) : 0;
fpscr |= _context.GetFPstateFlag(FPState.CFlag) ? (1u << (int)FPState.CFlag) : 0;
fpscr |= _context.GetFPstateFlag(FPState.VFlag) ? (1u << (int)FPState.VFlag) : 0;
return fpscr;
}
private void SetFpscr(uint fpscr)
{
_context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
_context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
_context.SetFPstateFlag(FPState.NFlag, (fpscr & (1u << (int)FPState.NFlag)) != 0);
_context.SetFPstateFlag(FPState.ZFlag, (fpscr & (1u << (int)FPState.ZFlag)) != 0);
_context.SetFPstateFlag(FPState.CFlag, (fpscr & (1u << (int)FPState.CFlag)) != 0);
_context.SetFPstateFlag(FPState.VFlag, (fpscr & (1u << (int)FPState.VFlag)) != 0);
}
}
}