This commit is contained in:
HorrorTroll 2018-10-18 16:24:01 +07:00
commit 2c37642143
169 changed files with 9393 additions and 3184 deletions

View file

@ -278,15 +278,18 @@ namespace ChocolArm64
SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg));
SetA64("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg));
SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", AInstEmit.Fmaxnm_V, typeof(AOpCodeSimdReg));
SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmaxp_V, typeof(AOpCodeSimdReg));
SetA64("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg));
SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg));
SetA64("010111111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Se, typeof(AOpCodeSimdRegElemF));
SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fminp_V, typeof(AOpCodeSimdReg));
SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Se, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
SetA64("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011111<xxxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("010111111xxxxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Se, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg));
SetA64("0x0011111<<xxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011111<xxxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
SetA64("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
SetA64("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
@ -296,9 +299,13 @@ namespace ChocolArm64
SetA64("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
SetA64("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
SetA64("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
SetA64("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
SetA64("010111111xxxxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
SetA64("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
SetA64("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011111<xxxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("010111100x1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_S, typeof(AOpCodeSimdReg));
SetA64("011111111xxxxxxx1001x0xxxxxxxxxx", AInstEmit.Fmulx_Se, typeof(AOpCodeSimdRegElemF));
SetA64("0>0011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_V, typeof(AOpCodeSimdReg));
SetA64("0>1011111<xxxxxx1001x0xxxxxxxxxx", AInstEmit.Fmulx_Ve, typeof(AOpCodeSimdRegElemF));
SetA64("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd));
SetA64("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd));
SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg));
@ -308,6 +315,7 @@ namespace ChocolArm64
SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd));
SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg));
SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg));
SetA64("010111101x100001111110xxxxxxxxxx", AInstEmit.Frecpx_S, typeof(AOpCodeSimd));
SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd));
SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd));
@ -325,6 +333,7 @@ namespace ChocolArm64
SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg));
SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg));
SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
SetA64("0>1011101<100001111110xxxxxxxxxx", AInstEmit.Fsqrt_V, typeof(AOpCodeSimd));
SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns));
@ -395,6 +404,7 @@ namespace ChocolArm64
SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Sminp_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110000xxxxx001011xxxxxxxxxx", AInstEmit.Smov_S, typeof(AOpCodeSimdIns));
SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd));
SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd));

View file

@ -2,6 +2,8 @@ using System.Runtime.Intrinsics.X86;
public static class AOptimizations
{
internal static bool FastFP = true;
private static bool UseAllSseIfAvailable = true;
private static bool UseSseIfAvailable = true;
@ -13,4 +15,4 @@ public static class AOptimizations
internal static bool UseSse2 = (UseAllSseIfAvailable && UseSse2IfAvailable) && Sse2.IsSupported;
internal static bool UseSse41 = (UseAllSseIfAvailable && UseSse41IfAvailable) && Sse41.IsSupported;
internal static bool UseSse42 = (UseAllSseIfAvailable && UseSse42IfAvailable) && Sse42.IsSupported;
}
}

View file

@ -8,9 +8,14 @@ namespace ChocolArm64
{
class ATranslatorCache
{
private const int MaxTotalSize = 2 * 1024 * 256;
private const int MaxTimeDelta = 30000;
private const int MinCallCountForUpdate = 1000;
//Maximum size of the cache, in bytes, measured in ARM code size.
private const int MaxTotalSize = 4 * 1024 * 256;
//Minimum time required in milliseconds for a method to be eligible for deletion.
private const int MinTimeDelta = 2 * 60000;
//Minimum number of calls required to update the timestamp.
private const int MinCallCountForUpdate = 250;
private class CacheBucket
{
@ -134,7 +139,7 @@ namespace ChocolArm64
int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp);
if ((uint)TimeDelta <= (uint)MaxTimeDelta)
if ((uint)TimeDelta <= (uint)MinTimeDelta)
{
break;
}

View file

@ -8,15 +8,26 @@ namespace ChocolArm64.Decoder
public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{
if ((Size & 1) != 0)
switch ((OpCode >> 21) & 3) // sz:L
{
Index = (OpCode >> 11) & 1;
}
else
{
Index = (OpCode >> 21) & 1 |
(OpCode >> 10) & 2;
case 0: // H:0
Index = (OpCode >> 10) & 2; // 0, 2
break;
case 1: // H:1
Index = (OpCode >> 10) & 2;
Index++; // 1, 3
break;
case 2: // H
Index = (OpCode >> 11) & 1; // 0, 1
break;
default: Emitter = AInstEmit.Und; return;
}
}
}
}
}

View file

@ -4,6 +4,7 @@ using ChocolArm64.Translation;
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
using static ChocolArm64.Instruction.AInstEmitAluHelper;
@ -117,9 +118,18 @@ namespace ChocolArm64.Instruction
Context.EmitLdintzr(Op.Rn);
Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64);
if (Lzcnt.IsSupported)
{
Type TValue = Op.RegisterSize == ARegisterSize.Int32 ? typeof(uint) : typeof(ulong);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros));
Context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { TValue }));
}
else
{
Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros));
}
Context.EmitStintzr(Op.Rd);
}

File diff suppressed because it is too large Load diff

View file

@ -23,11 +23,11 @@ namespace ChocolArm64.Instruction
{
if (Op.Size < 3 && AOptimizations.UseSse2)
{
EmitSse2Call(Context, nameof(Sse2.CompareEqual));
EmitSse2Op(Context, nameof(Sse2.CompareEqual));
}
else if (Op.Size == 3 && AOptimizations.UseSse41)
{
EmitSse41Call(Context, nameof(Sse41.CompareEqual));
EmitSse41Op(Context, nameof(Sse41.CompareEqual));
}
else
{
@ -61,11 +61,11 @@ namespace ChocolArm64.Instruction
{
if (Op.Size < 3 && AOptimizations.UseSse2)
{
EmitSse2Call(Context, nameof(Sse2.CompareGreaterThan));
EmitSse2Op(Context, nameof(Sse2.CompareGreaterThan));
}
else if (Op.Size == 3 && AOptimizations.UseSse42)
{
EmitSse42Call(Context, nameof(Sse42.CompareGreaterThan));
EmitSse42Op(Context, nameof(Sse42.CompareGreaterThan));
}
else
{
@ -158,7 +158,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareEqualScalar));
EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareEqualScalar));
}
else
{
@ -171,7 +171,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareEqual));
EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareEqual));
}
else
{
@ -184,7 +184,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar));
EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar));
}
else
{
@ -197,7 +197,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqual));
EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqual));
}
else
{
@ -210,7 +210,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanScalar));
EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanScalar));
}
else
{
@ -223,7 +223,7 @@ namespace ChocolArm64.Instruction
if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse
&& AOptimizations.UseSse2)
{
EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThan));
EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThan));
}
else
{

View file

@ -3,6 +3,8 @@ using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
@ -14,11 +16,48 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
if (AOptimizations.UseSse2)
{
if (Op.Size == 1 && Op.Opc == 0)
{
//Double -> Single.
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero));
EmitFloatCast(Context, Op.Opc);
EmitLdvecWithCastToDouble(Context, Op.Rn);
EmitScalarSetF(Context, Op.Rd, Op.Opc);
Type[] Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<double>) };
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), Types));
Context.EmitStvec(Op.Rd);
}
else if (Op.Size == 0 && Op.Opc == 1)
{
//Single -> Double.
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleZero));
Context.EmitLdvec(Op.Rn);
Type[] Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<float>) };
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), Types));
EmitStvecWithCastFromDouble(Context, Op.Rd);
}
else
{
//Invalid encoding.
throw new InvalidOperationException();
}
}
else
{
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
EmitFloatCast(Context, Op.Opc);
EmitScalarSetF(Context, Op.Rd, Op.Opc);
}
}
public static void Fcvtas_Gp(AILEmitterCtx Context)

View file

@ -4,7 +4,6 @@ using ChocolArm64.Translation;
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@ -12,6 +11,38 @@ namespace ChocolArm64.Instruction
{
static class AInstEmitSimdHelper
{
public static readonly Type[] IntTypesPerSizeLog2 = new Type[]
{
typeof(sbyte),
typeof(short),
typeof(int),
typeof(long)
};
public static readonly Type[] UIntTypesPerSizeLog2 = new Type[]
{
typeof(byte),
typeof(ushort),
typeof(uint),
typeof(ulong)
};
public static readonly Type[] VectorIntTypesPerSizeLog2 = new Type[]
{
typeof(Vector128<sbyte>),
typeof(Vector128<short>),
typeof(Vector128<int>),
typeof(Vector128<long>)
};
public static readonly Type[] VectorUIntTypesPerSizeLog2 = new Type[]
{
typeof(Vector128<byte>),
typeof(Vector128<ushort>),
typeof(Vector128<uint>),
typeof(Vector128<ulong>)
};
[Flags]
public enum OperFlags
{
@ -36,56 +67,32 @@ namespace ChocolArm64.Instruction
return (8 << (Op.Size + 1)) - Op.Imm;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EmitSse2Call(AILEmitterCtx Context, string Name)
public static void EmitSse2Op(AILEmitterCtx Context, string Name)
{
EmitSseCall(Context, Name, typeof(Sse2));
EmitSseOp(Context, Name, typeof(Sse2));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EmitSse41Call(AILEmitterCtx Context, string Name)
public static void EmitSse41Op(AILEmitterCtx Context, string Name)
{
EmitSseCall(Context, Name, typeof(Sse41));
EmitSseOp(Context, Name, typeof(Sse41));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EmitSse42Call(AILEmitterCtx Context, string Name)
public static void EmitSse42Op(AILEmitterCtx Context, string Name)
{
EmitSseCall(Context, Name, typeof(Sse42));
EmitSseOp(Context, Name, typeof(Sse42));
}
private static void EmitSseCall(AILEmitterCtx Context, string Name, Type Type)
private static void EmitSseOp(AILEmitterCtx Context, string Name, Type Type)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
void Ldvec(int Reg)
{
Context.EmitLdvec(Reg);
EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size);
switch (Op.Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break;
}
}
Ldvec(Op.Rn);
Type BaseType = null;
switch (Op.Size)
{
case 0: BaseType = typeof(Vector128<sbyte>); break;
case 1: BaseType = typeof(Vector128<short>); break;
case 2: BaseType = typeof(Vector128<int>); break;
case 3: BaseType = typeof(Vector128<long>); break;
}
Type BaseType = VectorIntTypesPerSizeLog2[Op.Size];
if (Op is AOpCodeSimdReg BinOp)
{
Ldvec(BinOp.Rm);
EmitLdvecWithSignedCast(Context, BinOp.Rm, Op.Size);
Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType }));
}
@ -94,15 +101,7 @@ namespace ChocolArm64.Instruction
Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType }));
}
switch (Op.Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break;
}
Context.EmitStvec(Op.Rd);
EmitStvecWithSignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
@ -110,17 +109,91 @@ namespace ChocolArm64.Instruction
}
}
public static void EmitScalarSseOrSse2CallF(AILEmitterCtx Context, string Name)
public static void EmitLdvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size)
{
EmitSseOrSse2CallF(Context, Name, true);
Context.EmitLdvec(Reg);
switch (Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break;
default: throw new ArgumentOutOfRangeException(nameof(Size));
}
}
public static void EmitVectorSseOrSse2CallF(AILEmitterCtx Context, string Name)
public static void EmitLdvecWithCastToDouble(AILEmitterCtx Context, int Reg)
{
EmitSseOrSse2CallF(Context, Name, false);
Context.EmitLdvec(Reg);
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble));
}
public static void EmitSseOrSse2CallF(AILEmitterCtx Context, string Name, bool Scalar)
public static void EmitStvecWithCastFromDouble(AILEmitterCtx Context, int Reg)
{
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle));
Context.EmitStvec(Reg);
}
public static void EmitLdvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size)
{
Context.EmitLdvec(Reg);
switch (Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToByte)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt16)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt32)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt64)); break;
default: throw new ArgumentOutOfRangeException(nameof(Size));
}
}
public static void EmitStvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size)
{
switch (Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break;
default: throw new ArgumentOutOfRangeException(nameof(Size));
}
Context.EmitStvec(Reg);
}
public static void EmitStvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size)
{
switch (Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorByteToSingle)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt16ToSingle)); break;
case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt32ToSingle)); break;
case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt64ToSingle)); break;
default: throw new ArgumentOutOfRangeException(nameof(Size));
}
Context.EmitStvec(Reg);
}
public static void EmitScalarSseOrSse2OpF(AILEmitterCtx Context, string Name)
{
EmitSseOrSse2OpF(Context, Name, true);
}
public static void EmitVectorSseOrSse2OpF(AILEmitterCtx Context, string Name)
{
EmitSseOrSse2OpF(Context, Name, false);
}
public static void EmitSseOrSse2OpF(AILEmitterCtx Context, string Name, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -233,25 +306,19 @@ namespace ChocolArm64.Instruction
int SizeF = Op.Size & 1;
Context.EmitLdc_I4((int)RoundMode);
MethodInfo MthdInfo;
Type[] Types = new Type[] { null, typeof(MidpointRounding) };
Types[0] = SizeF == 0
? typeof(float)
: typeof(double);
if (SizeF == 0)
{
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) });
}
else /* if (SizeF == 1) */
{
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) });
}
Context.EmitLdc_I4((int)RoundMode);
Context.EmitCall(MthdInfo);
}
@ -275,24 +342,17 @@ namespace ChocolArm64.Instruction
Context.EmitCall(MthdInfo);
}
public static void EmitBinarySoftFloatCall(AILEmitterCtx Context, string Name)
public static void EmitSoftFloatCall(AILEmitterCtx Context, string Name)
{
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
Type Type = (Op.Size & 1) == 0
? typeof(ASoftFloat_32)
: typeof(ASoftFloat_64);
MethodInfo MthdInfo;
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
if (SizeF == 0)
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float), typeof(float) });
}
else /* if (SizeF == 1) */
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double), typeof(double) });
}
Context.EmitCall(MthdInfo);
Context.EmitCall(Type, Name);
}
public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
@ -813,6 +873,42 @@ namespace ChocolArm64.Instruction
}
}
public static void EmitVectorPairwiseOpF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
int Words = Op.GetBitsCount() >> 4;
int Pairs = Words >> SizeF + 2;
for (int Index = 0; Index < Pairs; Index++)
{
int Idx = Index << 1;
EmitVectorExtractF(Context, Op.Rn, Idx, SizeF);
EmitVectorExtractF(Context, Op.Rn, Idx + 1, SizeF);
Emit();
EmitVectorExtractF(Context, Op.Rm, Idx, SizeF);
EmitVectorExtractF(Context, Op.Rm, Idx + 1, SizeF);
Emit();
EmitVectorInsertTmpF(Context, Pairs + Index, SizeF);
EmitVectorInsertTmpF(Context, Index, SizeF);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
[Flags]
public enum SaturatingFlags
{
@ -1147,8 +1243,21 @@ namespace ChocolArm64.Instruction
public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size)
{
EmitVectorZeroAll(Context, Reg);
EmitVectorInsertF(Context, Reg, 0, Size);
if (AOptimizations.UseSse41 && Size == 0)
{
//If the type is float, we can perform insertion and
//zero the upper bits with a single instruction (INSERTPS);
Context.EmitLdvec(Reg);
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Sse41VectorInsertScalarSingle));
Context.EmitStvec(Reg);
}
else
{
EmitVectorZeroAll(Context, Reg);
EmitVectorInsertF(Context, Reg, 0, Size);
}
}
public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size)
@ -1199,8 +1308,17 @@ namespace ChocolArm64.Instruction
public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd)
{
EmitVectorZeroLower(Context, Rd);
EmitVectorZeroUpper(Context, Rd);
if (AOptimizations.UseSse2)
{
AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero));
Context.EmitStvec(Rd);
}
else
{
EmitVectorZeroLower(Context, Rd);
EmitVectorZeroUpper(Context, Rd);
}
}
public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd)
@ -1213,9 +1331,32 @@ namespace ChocolArm64.Instruction
EmitVectorInsertTmp(Context, 0, 3, 0);
}
public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd)
public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Reg)
{
EmitVectorInsert(Context, Rd, 1, 3, 0);
if (AOptimizations.UseSse2)
{
//TODO: Use MoveScalar once it is fixed, as of the
//time of writing it just crashes the JIT.
EmitLdvecWithUnsignedCast(Context, Reg, 3);
Type[] Types = new Type[] { typeof(Vector128<ulong>), typeof(byte) };
//Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), Types));
Context.EmitLdc_I4(8);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), Types));
Context.EmitLdc_I4(8);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), Types));
EmitStvecWithUnsignedCast(Context, Reg, 3);
}
else
{
EmitVectorInsert(Context, Reg, 1, 3, 0);
}
}
public static void EmitVectorZero32_128(AILEmitterCtx Context, int Reg)

View file

@ -15,7 +15,7 @@ namespace ChocolArm64.Instruction
{
if (AOptimizations.UseSse2)
{
EmitSse2Call(Context, nameof(Sse2.And));
EmitSse2Op(Context, nameof(Sse2.And));
}
else
{
@ -25,11 +25,36 @@ namespace ChocolArm64.Instruction
public static void Bic_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () =>
if (AOptimizations.UseSse2)
{
Context.Emit(OpCodes.Not);
Context.Emit(OpCodes.And);
});
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
Type[] Types = new Type[]
{
VectorUIntTypesPerSizeLog2[Op.Size],
VectorUIntTypesPerSizeLog2[Op.Size]
};
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorBinaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Not);
Context.Emit(OpCodes.And);
});
}
}
public static void Bic_Vi(AILEmitterCtx Context)
@ -55,59 +80,124 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Bytes = Op.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
if (AOptimizations.UseSse2)
{
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
Context.Emit(OpCodes.Xor);
EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size);
if (NotRm)
Type[] Types = new Type[]
{
Context.Emit(OpCodes.Not);
VectorUIntTypesPerSizeLog2[Op.Size],
VectorUIntTypesPerSizeLog2[Op.Size]
};
EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types));
string Name = NotRm ? nameof(Sse2.AndNot) : nameof(Sse2.And);
Context.EmitCall(typeof(Sse2).GetMethod(Name, Types));
EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
int Bytes = Op.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
Context.Emit(OpCodes.Xor);
EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size);
if (NotRm)
{
Context.Emit(OpCodes.Not);
}
Context.Emit(OpCodes.And);
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
Context.Emit(OpCodes.Xor);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
Context.Emit(OpCodes.And);
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
Context.Emit(OpCodes.Xor);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
}
public static void Bsl_V(AILEmitterCtx Context)
{
EmitVectorTernaryOpZx(Context, () =>
if (AOptimizations.UseSse2)
{
Context.EmitSttmp();
Context.EmitLdtmp();
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
Context.Emit(OpCodes.Xor);
Context.Emit(OpCodes.And);
Type[] Types = new Type[]
{
VectorUIntTypesPerSizeLog2[Op.Size],
VectorUIntTypesPerSizeLog2[Op.Size]
};
Context.EmitLdtmp();
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size);
Context.Emit(OpCodes.Xor);
});
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types));
EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), Types));
EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size);
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorTernaryOpZx(Context, () =>
{
Context.EmitSttmp();
Context.EmitLdtmp();
Context.Emit(OpCodes.Xor);
Context.Emit(OpCodes.And);
Context.EmitLdtmp();
Context.Emit(OpCodes.Xor);
});
}
}
public static void Eor_V(AILEmitterCtx Context)
{
if (AOptimizations.UseSse2)
{
EmitSse2Call(Context, nameof(Sse2.Xor));
EmitSse2Op(Context, nameof(Sse2.Xor));
}
else
{
@ -133,7 +223,7 @@ namespace ChocolArm64.Instruction
{
if (AOptimizations.UseSse2)
{
EmitSse2Call(Context, nameof(Sse2.Or));
EmitSse2Op(Context, nameof(Sse2.Or));
}
else
{

View file

@ -3,6 +3,7 @@ using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
@ -14,19 +15,44 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
int Bytes = Op.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
if (AOptimizations.UseSse2)
{
Context.EmitLdintzr(Op.Rn);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
switch (Op.Size)
{
case 0: Context.Emit(OpCodes.Conv_U1); break;
case 1: Context.Emit(OpCodes.Conv_U2); break;
case 2: Context.Emit(OpCodes.Conv_U4); break;
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
Type[] Types = new Type[] { UIntTypesPerSizeLog2[Op.Size] };
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorZeroUpper(Context, Op.Rd);
int Bytes = Op.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
{
Context.EmitLdintzr(Op.Rn);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
}
@ -223,6 +249,17 @@ namespace ChocolArm64.Instruction
EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not));
}
public static void Smov_S(AILEmitterCtx Context)
{
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
EmitVectorExtractSx(Context, Op.Rn, Op.DstIndex, Op.Size);
EmitIntZeroUpperIfNeeded(Context);
Context.EmitStintzr(Op.Rd);
}
public static void Tbl_V(AILEmitterCtx Context)
{
AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp;
@ -295,25 +332,91 @@ namespace ChocolArm64.Instruction
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
if (Part != 0)
if (AOptimizations.UseSse41 && Op.Size < 2)
{
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
void EmitZeroVector()
{
switch (Op.Size)
{
case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16Zero)); break;
case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32Zero)); break;
}
}
//For XTN, first operand is source, second operand is 0.
//For XTN2, first operand is 0, second operand is source.
if (Part != 0)
{
EmitZeroVector();
}
EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size + 1);
//Set mask to discard the upper half of the wide elements.
switch (Op.Size)
{
case 0: Context.EmitLdc_I4(0x00ff); break;
case 1: Context.EmitLdc_I4(0x0000ffff); break;
}
Type WideType = IntTypesPerSizeLog2[Op.Size + 1];
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { WideType }));
WideType = VectorIntTypesPerSizeLog2[Op.Size + 1];
Type[] WideTypes = new Type[] { WideType, WideType };
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), WideTypes));
if (Part == 0)
{
EmitZeroVector();
}
//Pack values with signed saturation, the signed saturation shouldn't
//saturate anything since the upper bits were masked off.
Type SseType = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41);
Context.EmitCall(SseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), WideTypes));
if (Part != 0)
{
//For XTN2, we additionally need to discard the upper bits
//of the target register and OR the result with it.
EmitVectorZeroUpper(Context, Op.Rd);
EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size);
Type NarrowType = VectorUIntTypesPerSizeLog2[Op.Size];
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { NarrowType, NarrowType }));
}
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
}
for (int Index = 0; Index < Elems; Index++)
else
{
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
if (Part != 0)
{
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
}
EmitVectorInsertTmp(Context, Part + Index, Op.Size);
}
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
EmitVectorInsertTmp(Context, Part + Index, Op.Size);
}
if (Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
}
@ -329,7 +432,8 @@ namespace ChocolArm64.Instruction
private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context)
{
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32 ||
Context.CurrOp.RegisterSize == ARegisterSize.SIMD64)
{
Context.Emit(OpCodes.Conv_U4);
Context.Emit(OpCodes.Conv_U8);
@ -394,28 +498,64 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Words = Op.GetBitsCount() >> 4;
int Pairs = Words >> Op.Size;
int Base = Part != 0 ? Pairs : 0;
for (int Index = 0; Index < Pairs; Index++)
if (AOptimizations.UseSse2)
{
int Idx = Index << 1;
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size);
EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size);
EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size);
Type[] Types = new Type[]
{
VectorUIntTypesPerSizeLog2[Op.Size],
VectorUIntTypesPerSizeLog2[Op.Size]
};
EmitVectorInsertTmp(Context, Idx + 1, Op.Size);
EmitVectorInsertTmp(Context, Idx, Op.Size);
string Name = Part == 0 || (Part != 0 && Op.RegisterSize == ARegisterSize.SIMD64)
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
Context.EmitCall(typeof(Sse2).GetMethod(Name, Types));
if (Op.RegisterSize == ARegisterSize.SIMD64 && Part != 0)
{
Context.EmitLdc_I4(8);
Type[] ShTypes = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) };
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), ShTypes));
}
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64 && Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
else
{
EmitVectorZeroUpper(Context, Op.Rd);
int Words = Op.GetBitsCount() >> 4;
int Pairs = Words >> Op.Size;
int Base = Part != 0 ? Pairs : 0;
for (int Index = 0; Index < Pairs; Index++)
{
int Idx = Index << 1;
EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size);
EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size);
EmitVectorInsertTmp(Context, Idx + 1, Op.Size);
EmitVectorInsertTmp(Context, Idx, Op.Size);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
}
}

View file

@ -3,6 +3,7 @@ using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
@ -31,12 +32,32 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
EmitVectorUnaryOpZx(Context, () =>
if (AOptimizations.UseSse2 && Op.Size > 0)
{
Type[] Types = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) };
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
Context.EmitLdc_I4(GetImmShl(Op));
Context.Emit(OpCodes.Shl);
});
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorUnaryOpZx(Context, () =>
{
Context.EmitLdc_I4(GetImmShl(Op));
Context.Emit(OpCodes.Shl);
});
}
}
public static void Shll_V(AILEmitterCtx Context)
@ -167,7 +188,30 @@ namespace ChocolArm64.Instruction
public static void Sshr_V(AILEmitterCtx Context)
{
EmitShrImmOp(Context, ShrImmFlags.VectorSx);
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
if (AOptimizations.UseSse2 && Op.Size > 0
&& Op.Size < 3)
{
Type[] Types = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) };
EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size);
Context.EmitLdc_I4(GetImmShr(Op));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), Types));
EmitStvecWithSignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitShrImmOp(Context, ShrImmFlags.VectorSx);
}
}
public static void Ssra_S(AILEmitterCtx Context)
@ -177,7 +221,33 @@ namespace ChocolArm64.Instruction
public static void Ssra_V(AILEmitterCtx Context)
{
EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate);
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
if (AOptimizations.UseSse2 && Op.Size > 0
&& Op.Size < 3)
{
Type[] TypesSra = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) };
Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] };
EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size);
EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size);
Context.EmitLdc_I4(GetImmShr(Op));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesSra));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd));
EmitStvecWithSignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate);
}
}
public static void Uqrshrn_S(AILEmitterCtx Context)
@ -239,7 +309,29 @@ namespace ChocolArm64.Instruction
public static void Ushr_V(AILEmitterCtx Context)
{
EmitShrImmOp(Context, ShrImmFlags.VectorZx);
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
if (AOptimizations.UseSse2 && Op.Size > 0)
{
Type[] Types = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) };
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
Context.EmitLdc_I4(GetImmShr(Op));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), Types));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitShrImmOp(Context, ShrImmFlags.VectorZx);
}
}
public static void Usra_S(AILEmitterCtx Context)
@ -249,7 +341,32 @@ namespace ChocolArm64.Instruction
public static void Usra_V(AILEmitterCtx Context)
{
EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate);
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
if (AOptimizations.UseSse2 && Op.Size > 0)
{
Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) };
Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] };
EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size);
EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size);
Context.EmitLdc_I4(GetImmShr(Op));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesSrl));
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd));
EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
else
{
EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate);
}
}
private static void EmitVectorShl(AILEmitterCtx Context, bool Signed)

View file

@ -386,7 +386,7 @@ namespace ChocolArm64.Instruction
#endregion
#region "Count"
public static ulong CountLeadingSigns(ulong Value, int Size)
public static ulong CountLeadingSigns(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
Value ^= Value >> 1;
@ -405,9 +405,9 @@ namespace ChocolArm64.Instruction
private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
public static ulong CountLeadingZeros(ulong Value, int Size)
public static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
if (Value == 0)
if (Value == 0ul)
{
return (ulong)Size;
}
@ -426,12 +426,17 @@ namespace ChocolArm64.Instruction
return (ulong)Count;
}
public static uint CountSetBits8(uint Value)
public static ulong CountSetBits8(ulong Value) // "Size" is 8 (SIMD&FP Inst.).
{
Value = ((Value >> 1) & 0x55) + (Value & 0x55);
Value = ((Value >> 2) & 0x33) + (Value & 0x33);
if (Value == 0xfful)
{
return 8ul;
}
return (Value >> 4) + (Value & 0x0f);
Value = ((Value >> 1) & 0x55ul) + (Value & 0x55ul);
Value = ((Value >> 2) & 0x33ul) + (Value & 0x33ul);
return (Value >> 4) + (Value & 0x0ful);
}
#endregion

File diff suppressed because it is too large Load diff

View file

@ -227,7 +227,16 @@ namespace ChocolArm64.Instruction
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double VectorExtractDouble(Vector128<float> Vector, byte Index)
{
return BitConverter.Int64BitsToDouble(VectorExtractIntSx(Vector, Index, 3));
if (Sse41.IsSupported)
{
return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast<float, long>(Vector), Index));
}
else if (Sse2.IsSupported)
{
return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(Vector, Index, 3));
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -235,41 +244,49 @@ namespace ChocolArm64.Instruction
{
if (Sse41.IsSupported)
{
switch (Size)
if (Size == 0)
{
case 0:
return (sbyte)Sse41.Extract(Sse.StaticCast<float, byte>(Vector), Index);
case 1:
return (short)Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), Index);
case 2:
return Sse41.Extract(Sse.StaticCast<float, int>(Vector), Index);
case 3:
return Sse41.Extract(Sse.StaticCast<float, long>(Vector), Index);
return (sbyte)Sse41.Extract(Sse.StaticCast<float, byte>(Vector), Index);
}
else if (Size == 1)
{
return (short)Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), Index);
}
else if (Size == 2)
{
return Sse41.Extract(Sse.StaticCast<float, int>(Vector), Index);
}
else if (Size == 3)
{
return Sse41.Extract(Sse.StaticCast<float, long>(Vector), Index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
else if (Sse2.IsSupported)
{
switch (Size)
if (Size == 0)
{
case 0:
return (sbyte)VectorExtractIntZx(Vector, Index, Size);
case 1:
return (short)VectorExtractIntZx(Vector, Index, Size);
case 2:
return (int)VectorExtractIntZx(Vector, Index, Size);
case 3:
return (long)VectorExtractIntZx(Vector, Index, Size);
return (sbyte)VectorExtractIntZx(Vector, Index, Size);
}
else if (Size == 1)
{
return (short)VectorExtractIntZx(Vector, Index, Size);
}
else if (Size == 2)
{
return (int)VectorExtractIntZx(Vector, Index, Size);
}
else if (Size == 3)
{
return (long)VectorExtractIntZx(Vector, Index, Size);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
throw new PlatformNotSupportedException();
@ -280,22 +297,26 @@ namespace ChocolArm64.Instruction
{
if (Sse41.IsSupported)
{
switch (Size)
if (Size == 0)
{
case 0:
return Sse41.Extract(Sse.StaticCast<float, byte>(Vector), Index);
case 1:
return Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), Index);
case 2:
return Sse41.Extract(Sse.StaticCast<float, uint>(Vector), Index);
case 3:
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), Index);
return Sse41.Extract(Sse.StaticCast<float, byte>(Vector), Index);
}
else if (Size == 1)
{
return Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), Index);
}
else if (Size == 2)
{
return Sse41.Extract(Sse.StaticCast<float, uint>(Vector), Index);
}
else if (Size == 3)
{
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), Index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
else if (Sse2.IsSupported)
{
@ -305,35 +326,35 @@ namespace ChocolArm64.Instruction
ushort Value = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)ShortIdx);
switch (Size)
if (Size == 0)
{
case 0:
return (byte)(Value >> (Index & 1) * 8);
case 1:
return Value;
case 2:
case 3:
{
ushort Value1 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 1));
if (Size == 2)
{
return (uint)(Value | (Value1 << 16));
}
ushort Value2 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 2));
ushort Value3 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 3));
return ((ulong)Value << 0) |
((ulong)Value1 << 16) |
((ulong)Value2 << 32) |
((ulong)Value3 << 48);
}
return (byte)(Value >> (Index & 1) * 8);
}
else if (Size == 1)
{
return Value;
}
else if (Size == 2 || Size == 3)
{
ushort Value1 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 1));
throw new ArgumentOutOfRangeException(nameof(Size));
if (Size == 2)
{
return (uint)(Value | (Value1 << 16));
}
ushort Value2 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 2));
ushort Value3 = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)(ShortIdx + 3));
return ((ulong)Value << 0) |
((ulong)Value1 << 16) |
((ulong)Value2 << 32) |
((ulong)Value3 << 48);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
throw new PlatformNotSupportedException();
@ -370,22 +391,26 @@ namespace ChocolArm64.Instruction
{
if (Sse41.IsSupported)
{
switch (Size)
if (Size == 0)
{
case 0:
return Sse.StaticCast<byte, float>(Sse41.Insert(Sse.StaticCast<float, byte>(Vector), (byte)Value, Index));
case 1:
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(Vector), (ushort)Value, Index));
case 2:
return Sse.StaticCast<uint, float>(Sse41.Insert(Sse.StaticCast<float, uint>(Vector), (uint)Value, Index));
case 3:
return Sse.StaticCast<ulong, float>(Sse41.Insert(Sse.StaticCast<float, ulong>(Vector), Value, Index));
return Sse.StaticCast<byte, float>(Sse41.Insert(Sse.StaticCast<float, byte>(Vector), (byte)Value, Index));
}
else if (Size == 1)
{
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(Vector), (ushort)Value, Index));
}
else if (Size == 2)
{
return Sse.StaticCast<uint, float>(Sse41.Insert(Sse.StaticCast<float, uint>(Vector), (uint)Value, Index));
}
else if (Size == 3)
{
return Sse.StaticCast<ulong, float>(Sse41.Insert(Sse.StaticCast<float, ulong>(Vector), Value, Index));
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
else if (Sse2.IsSupported)
{
@ -395,41 +420,39 @@ namespace ChocolArm64.Instruction
? Index >> 1
: Index << (Size - 1);
switch (Size)
if (Size == 0)
{
case 0:
{
ushort ShortVal = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)ShortIdx);
ushort ShortVal = Sse2.Extract(Sse.StaticCast<float, ushort>(Vector), (byte)ShortIdx);
int Shift = (Index & 1) * 8;
int Shift = (Index & 1) * 8;
ShortVal &= (ushort)(0xff00 >> Shift);
ShortVal &= (ushort)(0xff00 >> Shift);
ShortVal |= (ushort)((byte)Value << Shift);
ShortVal |= (ushort)((byte)Value << Shift);
return Sse.StaticCast<ushort, float>(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx));
}
case 1:
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(Vector), (ushort)Value, Index));
case 2:
case 3:
{
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0));
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1));
if (Size == 3)
{
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2));
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3));
}
return Sse.StaticCast<ushort, float>(ShortVector);
}
return Sse.StaticCast<ushort, float>(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx));
}
else if (Size == 1)
{
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(Vector), (ushort)Value, Index));
}
else if (Size == 2 || Size == 3)
{
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0));
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1));
throw new ArgumentOutOfRangeException(nameof(Size));
if (Size == 3)
{
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2));
ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3));
}
return Sse.StaticCast<ushort, float>(ShortVector);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
throw new PlatformNotSupportedException();
@ -440,7 +463,29 @@ namespace ChocolArm64.Instruction
{
if (Sse41.IsSupported)
{
return Sse41.Insert(Vector, Value, (byte)(Index << 4));
//Note: The if/else if is necessary to enable the JIT to
//produce a single INSERTPS instruction instead of the
//jump table fallback.
if (Index == 0)
{
return Sse41.Insert(Vector, Value, 0x00);
}
else if (Index == 1)
{
return Sse41.Insert(Vector, Value, 0x10);
}
else if (Index == 2)
{
return Sse41.Insert(Vector, Value, 0x20);
}
else if (Index == 3)
{
return Sse41.Insert(Vector, Value, 0x30);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
}
else if (Sse2.IsSupported)
{
@ -460,6 +505,79 @@ namespace ChocolArm64.Instruction
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> Sse41VectorInsertScalarSingle(float Value, Vector128<float> Vector)
{
//Note: 0b1110 is the mask to zero the upper bits.
return Sse41.Insert(Vector, Value, 0b1110);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<sbyte> VectorSByteZero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<sbyte>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<short> VectorInt16Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<short>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<int> VectorInt32Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<int>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<long> VectorInt64Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<long>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorSingleZero()
{
if (Sse.IsSupported)
{
return Sse.SetZeroVector128();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> VectorDoubleZero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<double>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorZero32_128(Vector128<float> Vector)
{
@ -515,6 +633,50 @@ namespace ChocolArm64.Instruction
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<byte> VectorSingleToByte(Vector128<float> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, byte>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<ushort> VectorSingleToUInt16(Vector128<float> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, ushort>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<uint> VectorSingleToUInt32(Vector128<float> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, uint>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<ulong> VectorSingleToUInt64(Vector128<float> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, ulong>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> VectorSingleToDouble(Vector128<float> Vector)
{
@ -570,6 +732,50 @@ namespace ChocolArm64.Instruction
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorByteToSingle(Vector128<byte> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<byte, float>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt16ToSingle(Vector128<ushort> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<ushort, float>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt32ToSingle(Vector128<uint> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<uint, float>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt64ToSingle(Vector128<ulong> Vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<ulong, float>(Vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorDoubleToSingle(Vector128<double> Vector)
{

View file

@ -232,7 +232,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector32(long Position)
{
if (Sse.IsSupported)
@ -245,7 +245,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector64(long Position)
{
if (Sse2.IsSupported)
@ -365,7 +365,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector32(long Position, Vector128<float> Value)
{
if (Sse.IsSupported)
@ -378,7 +378,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector64(long Position, Vector128<float> Value)
{
if (Sse2.IsSupported)

View file

@ -182,7 +182,10 @@ namespace Ryujinx.Audio.OpenAL
{
foreach (Track Td in Tracks.Values)
{
Td.CallReleaseCallbackIfNeeded();
lock (Td)
{
Td.CallReleaseCallbackIfNeeded();
}
}
//If it's not slept it will waste cycles.

View file

@ -9,4 +9,8 @@
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Logging
namespace Ryujinx.Common.Logging
{
public enum LogClass
{
@ -22,6 +22,8 @@ namespace Ryujinx.HLE.Logging
ServiceFriend,
ServiceFs,
ServiceHid,
ServiceIrs,
ServiceLdr,
ServiceLm,
ServiceMm,
ServiceNfp,

View file

@ -1,6 +1,6 @@
using System;
namespace Ryujinx.HLE.Logging
namespace Ryujinx.Common.Logging
{
public class LogEventArgs : EventArgs
{

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Logging
namespace Ryujinx.Common.Logging
{
public enum LogLevel
{

View file

@ -2,18 +2,18 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.Logging
namespace Ryujinx.Common.Logging
{
public class Logger
public static class Logger
{
private bool[] EnabledLevels;
private bool[] EnabledClasses;
private static bool[] EnabledLevels;
private static bool[] EnabledClasses;
public event EventHandler<LogEventArgs> Updated;
public static event EventHandler<LogEventArgs> Updated;
private Stopwatch Time;
private static Stopwatch Time;
public Logger()
static Logger()
{
EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length];
EnabledClasses = new bool[Enum.GetNames(typeof(LogClass)).Length];
@ -33,50 +33,50 @@ namespace Ryujinx.HLE.Logging
Time.Start();
}
public void SetEnable(LogLevel Level, bool Enabled)
public static void SetEnable(LogLevel Level, bool Enabled)
{
EnabledLevels[(int)Level] = Enabled;
}
public void SetEnable(LogClass Class, bool Enabled)
public static void SetEnable(LogClass Class, bool Enabled)
{
EnabledClasses[(int)Class] = Enabled;
}
internal void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "")
public static void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "")
{
Print(LogLevel.Debug, Class, GetFormattedMessage(Class, Message, Caller));
}
internal void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "")
public static void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "")
{
Print(LogLevel.Stub, Class, GetFormattedMessage(Class, Message, Caller));
}
internal void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "")
public static void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "")
{
Print(LogLevel.Info, Class, GetFormattedMessage(Class, Message, Caller));
}
internal void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "")
public static void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "")
{
Print(LogLevel.Warning, Class, GetFormattedMessage(Class, Message, Caller));
}
internal void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "")
public static void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "")
{
Print(LogLevel.Error, Class, GetFormattedMessage(Class, Message, Caller));
}
private void Print(LogLevel Level, LogClass Class, string Message)
private static void Print(LogLevel Level, LogClass Class, string Message)
{
if (EnabledLevels[(int)Level] && EnabledClasses[(int)Class])
{
Updated?.Invoke(this, new LogEventArgs(Level, Time.Elapsed, Message));
Updated?.Invoke(null, new LogEventArgs(Level, Time.Elapsed, Message));
}
}
private string GetFormattedMessage(LogClass Class, string Message, string Caller)
private static string GetFormattedMessage(LogClass Class, string Message, string Caller)
{
return $"{Class} {Caller}: {Message}";
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

View file

@ -5,89 +5,61 @@ namespace Ryujinx.Graphics.Gal
[Flags]
public enum GalImageFormat
{
Snorm = 1 << 27,
Unorm = 1 << 28,
Sint = 1 << 29,
Uint = 1 << 30,
Sfloat = 1 << 31,
Astc2DStart,
Astc2D4x4,
Astc2D5x4,
Astc2D5x5,
Astc2D6x5,
Astc2D6x6,
Astc2D8x5,
Astc2D8x6,
Astc2D8x8,
Astc2D10x5,
Astc2D10x6,
Astc2D10x8,
Astc2D10x10,
Astc2D12x10,
Astc2D12x12,
Astc2DEnd,
TypeMask = Snorm | Unorm | Sint | Uint | Sfloat,
FormatMask = ~TypeMask,
ASTC_BEGIN = ASTC_4x4,
ASTC_4x4 = 1,
ASTC_5x4,
ASTC_5x5,
ASTC_6x5,
ASTC_6x6,
ASTC_8x5,
ASTC_8x6,
ASTC_8x8,
ASTC_10x5,
ASTC_10x6,
ASTC_10x8,
ASTC_10x10,
ASTC_12x10,
ASTC_12x12,
ASTC_END = ASTC_12x12,
R4G4,
R4G4B4A4,
B4G4R4A4,
A4B4G4R4,
R5G6B5,
B5G6R5,
R5G5B5A1,
B5G5R5A1,
A1R5G5B5,
RGBA4,
RGB565,
BGR5A1,
RGB5A1,
R8,
R8G8,
G8R8,
R8G8B8,
B8G8R8,
R8G8B8A8,
B8G8R8A8,
A8B8G8R8,
A8B8G8R8_SRGB,
A2R10G10B10,
A2B10G10R10,
RG8,
RGBA8,
BGRA8,
RGB10A2,
R16,
R16G16,
R16G16B16,
R16G16B16A16,
RG16,
RGBA16,
R32,
R32G32,
R32G32B32,
R32G32B32A32,
R64,
R64G64,
R64G64B64,
R64G64B64A64,
B10G11R11,
E5B9G9R9,
RG32,
RGBA32,
R11G11B10,
D16,
X8_D24,
D32,
S8,
D16_S8,
D24_S8,
D32_S8,
BC1_RGB,
BC1_RGBA,
D24S8,
D32S8,
BC1,
BC2,
BC3,
BC4,
BC5,
BC6H_SF16,
BC6H_UF16,
BC7,
ETC2_R8G8B8,
ETC2_R8G8B8A1,
ETC2_R8G8B8A8,
EAC_R11,
EAC_R11G11,
BptcSfloat,
BptcUfloat,
BptcUnorm,
Snorm = 1 << 26,
Unorm = 1 << 27,
Sint = 1 << 28,
Uint = 1 << 39,
Float = 1 << 30,
Srgb = 1 << 31,
TypeMask = Snorm | Unorm | Sint | Uint | Float | Srgb,
FormatMask = ~TypeMask
}
}

View file

@ -1,21 +1,28 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalVertexBinding
public struct ColorMaskRgba
{
//VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
private static readonly ColorMaskRgba _Default = new ColorMaskRgba()
{
Red = true,
Green = true,
Blue = true,
Alpha = true
};
public bool Enabled;
public int Stride;
public long VboKey;
public bool Instanced;
public int Divisor;
public GalVertexAttrib[] Attribs;
public static ColorMaskRgba Default => _Default;
public bool Red;
public bool Green;
public bool Blue;
public bool Alpha;
}
public class GalPipelineState
{
public const int Stages = 5;
public const int Stages = 5;
public const int ConstBuffersPerStage = 18;
public const int RenderTargetsCount = 8;
public long[][] ConstBufferKeys;
@ -38,6 +45,7 @@
public GalComparisonOp DepthFunc;
public bool StencilTestEnabled;
public bool StencilTwoSideEnabled;
public GalComparisonOp StencilBackFuncFunc;
public int StencilBackFuncRef;
@ -64,6 +72,9 @@
public GalBlendFactor BlendFuncSrcAlpha;
public GalBlendFactor BlendFuncDstAlpha;
public ColorMaskRgba ColorMask;
public ColorMaskRgba[] ColorMasks;
public bool PrimitiveRestartEnabled;
public uint PrimitiveRestartIndex;
@ -75,6 +86,8 @@
{
ConstBufferKeys[Stage] = new long[ConstBuffersPerStage];
}
ColorMasks = new ColorMaskRgba[RenderTargetsCount];
}
}
}

View file

@ -2,45 +2,45 @@ namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFormat
{
R32G32B32A32 = 0x1,
R16G16B16A16 = 0x3,
R32G32 = 0x4,
A8B8G8R8 = 0x8,
A2B10G10R10 = 0x9,
R16G16 = 0xc,
R32 = 0xf,
BC6H_SF16 = 0x10,
BC6H_UF16 = 0x11,
A4B4G4R4 = 0x12,
A1B5G5R5 = 0x14,
B5G6R5 = 0x15,
BC7U = 0x17,
G8R8 = 0x18,
R16 = 0x1b,
R8 = 0x1d,
BF10GF11RF11 = 0x21,
BC1 = 0x24,
BC2 = 0x25,
BC3 = 0x26,
BC4 = 0x27,
BC5 = 0x28,
Z24S8 = 0x29,
ZF32 = 0x2f,
ZF32_X24S8 = 0x30,
Z16 = 0x3a,
Astc2D4x4 = 0x40,
Astc2D5x5 = 0x41,
Astc2D6x6 = 0x42,
Astc2D8x8 = 0x44,
Astc2D10x10 = 0x45,
Astc2D12x12 = 0x46,
Astc2D5x4 = 0x50,
Astc2D6x5 = 0x51,
Astc2D8x6 = 0x52,
Astc2D10x8 = 0x53,
Astc2D12x10 = 0x54,
Astc2D8x5 = 0x55,
Astc2D10x5 = 0x56,
Astc2D10x6 = 0x57
RGBA32 = 0x1,
RGBA16 = 0x3,
RG32 = 0x4,
RGBA8 = 0x8,
RGB10A2 = 0x9,
RG16 = 0xc,
R32 = 0xf,
BptcSfloat = 0x10,
BptcUfloat = 0x11,
RGBA4 = 0x12,
RGB5A1 = 0x14,
RGB565 = 0x15,
BptcUnorm = 0x17,
RG8 = 0x18,
R16 = 0x1b,
R8 = 0x1d,
R11G11B10F = 0x21,
BC1 = 0x24,
BC2 = 0x25,
BC3 = 0x26,
BC4 = 0x27,
BC5 = 0x28,
D24S8 = 0x29,
D32F = 0x2f,
D32FX24S8 = 0x30,
D16 = 0x3a,
Astc2D4x4 = 0x40,
Astc2D5x5 = 0x41,
Astc2D6x6 = 0x42,
Astc2D8x8 = 0x44,
Astc2D10x10 = 0x45,
Astc2D12x12 = 0x46,
Astc2D5x4 = 0x50,
Astc2D6x5 = 0x51,
Astc2D8x6 = 0x52,
Astc2D10x8 = 0x53,
Astc2D12x10 = 0x54,
Astc2D8x5 = 0x55,
Astc2D10x5 = 0x56,
Astc2D10x6 = 0x57
}
}

View file

@ -1,10 +1,13 @@
using System;
namespace Ryujinx.Graphics.Gal
{
public struct GalVertexAttrib
{
public int Index { get; private set; }
public bool IsConst { get; private set; }
public int Offset { get; private set; }
public int Index { get; private set; }
public bool IsConst { get; private set; }
public int Offset { get; private set; }
public IntPtr Pointer { get; private set; }
public GalVertexAttribSize Size { get; private set; }
public GalVertexAttribType Type { get; private set; }
@ -15,12 +18,14 @@ namespace Ryujinx.Graphics.Gal
int Index,
bool IsConst,
int Offset,
IntPtr Pointer,
GalVertexAttribSize Size,
GalVertexAttribType Type,
bool IsBgra)
{
this.Index = Index;
this.IsConst = IsConst;
this.Pointer = Pointer;
this.Offset = Offset;
this.Size = Size;
this.Type = Type;

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalVertexBinding
{
//VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
public bool Enabled;
public int Stride;
public long VboKey;
public bool Instanced;
public int Divisor;
public GalVertexAttrib[] Attribs;
}
}

View file

@ -2,15 +2,15 @@
{
public enum GalZetaFormat
{
Z32Float = 0x0a,
Z16Unorm = 0x13,
S8Z24Unorm = 0x14,
Z24X8Unorm = 0x15,
Z24S8Unorm = 0x16,
Z24C8Unorm = 0x18,
Z32S8X24Float = 0x19,
Z24X8S8C8X16Unorm = 0x1d,
Z32X8C8X16Float = 0x1e,
Z32S8C8X16Float = 0x1f
D32Float = 0x0a,
D16Unorm = 0x13,
S8D24Unorm = 0x14,
D24X8Unorm = 0x15,
D24S8Unorm = 0x16,
D24C8Unorm = 0x18,
D32S8X24Float = 0x19,
D24X8S8C8X16Unorm = 0x1d,
D32X8C8X16Float = 0x1e,
D32S8C8X16Float = 0x1f
}
}

View file

@ -3,5 +3,8 @@
public interface IGalPipeline
{
void Bind(GalPipelineState State);
void ResetDepthMask();
void ResetColorMask(int Index);
}
}

View file

@ -10,7 +10,10 @@ namespace Ryujinx.Graphics.Gal
void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Red,
float Green,
float Blue,
float Alpha,
float Depth,
int Stencil);
@ -21,6 +24,7 @@ namespace Ryujinx.Graphics.Gal
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
void CreateIbo(long Key, int DataSize, byte[] Buffer);
void SetIndexArray(int Size, GalIndexFormat Format);

View file

@ -2,15 +2,17 @@ namespace Ryujinx.Graphics.Gal
{
public interface IGalRenderTarget
{
void BindColor(long Key, int Attachment, GalImage Image);
void Bind();
void BindColor(long Key, int Attachment);
void UnbindColor(int Attachment);
void BindZeta(long Key, GalImage Image);
void BindZeta(long Key);
void UnbindZeta();
void Set(long Key);
void Present(long Key);
void SetMap(int[] Map);
@ -18,7 +20,7 @@ namespace Ryujinx.Graphics.Gal
void SetWindowSize(int Width, int Height);
void SetViewport(int X, int Y, int Width, int Height);
void SetViewport(int Attachment, int X, int Y, int Width, int Height);
void Render();

View file

@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
}
throw new ArgumentException(nameof(FrontFace));
throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!");
}
public static CullFaceMode GetCullFace(GalCullFace CullFace)
@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
}
throw new ArgumentException(nameof(CullFace));
throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!");
}
public static StencilOp GetStencilOp(GalStencilOp Op)
@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
}
throw new ArgumentException(nameof(Op));
throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!");
}
public static DepthFunction GetDepthFunc(GalComparisonOp Func)
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalComparisonOp.Always: return DepthFunction.Always;
}
throw new ArgumentException(nameof(Func));
throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!");
}
public static StencilFunction GetStencilFunc(GalComparisonOp Func)
@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
throw new ArgumentException(nameof(Format));
throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!");
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
@ -98,8 +98,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalPrimitiveType.Triangles: return PrimitiveType.Triangles;
case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip;
case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan;
case GalPrimitiveType.Quads: return PrimitiveType.Quads;
case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip;
case GalPrimitiveType.Polygon: return PrimitiveType.Polygon;
case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency;
case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency;
@ -108,7 +106,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
throw new ArgumentException(nameof(Type));
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
}
public static ShaderType GetShaderType(GalShaderType Type)
@ -122,58 +120,61 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalShaderType.Fragment: return ShaderType.FragmentShader;
}
throw new ArgumentException(nameof(Type));
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
}
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
{
switch (Format)
{
case GalImageFormat.R32G32B32A32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.R32G32B32A32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.R32G32B32A32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.R16G16B16A16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.R16G16B16A16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.R16G16B16A16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.R32G32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.R32G32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.R32G32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.A8B8G8R8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.A8B8G8R8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.A8B8G8R8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.A8B8G8R8_SRGB: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.A4B4G4R4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.A2B10G10R10 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.A2B10G10R10 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.R32 | GalImageFormat.Sfloat: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float);
case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int);
case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt);
case GalImageFormat.A1R5G5B5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.B5G6R5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.R16G16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.R16G16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.R16G16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.R16G16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.R8G8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.R8G8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.R8G8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.R8G8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.R16 | GalImageFormat.Sfloat: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat);
case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short);
case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Byte);
case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort);
case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort);
case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte);
case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte);
case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte);
case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte);
case GalImageFormat.B10G11R11 | GalImageFormat.Sfloat: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalImageFormat.RGBA32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.RGBA32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.RGBA32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBA16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.RGBA16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.RGBA16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.BGRA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.RGBA4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float);
case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int);
case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt);
case GalImageFormat.RGB5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.RGB565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.RG16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.RG16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort);
case GalImageFormat.RG16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.RG8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.RG8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat);
case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short);
case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short);
case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort);
case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort);
case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte);
case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte);
case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte);
case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte);
case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalImageFormat.D24_S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D32 | GalImageFormat.Sfloat: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
case GalImageFormat.D32_S8 | GalImageFormat.Sfloat: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
@ -183,16 +184,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
switch (Format)
{
case GalImageFormat.BC6H_UF16 | GalImageFormat.Sfloat: return InternalFormat.CompressedRgbBptcUnsignedFloat;
case GalImageFormat.BC6H_SF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalImageFormat.BC7 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm;
case GalImageFormat.BC1_RGBA | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext;
case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1;
case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1;
case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2;
case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2;
case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat;
case GalImageFormat.BptcUnorm | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm;
case GalImageFormat.BptcUnorm | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaBptcUnorm;
case GalImageFormat.BC1 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalImageFormat.BC1 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt1Ext;
case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalImageFormat.BC2 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt3Ext;
case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext;
case GalImageFormat.BC3 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt5Ext;
case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1;
case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1;
case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2;
case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2;
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
@ -211,7 +216,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureSource.OneFloat: return All.One;
}
throw new ArgumentException(nameof(Source));
throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!");
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
@ -225,7 +230,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
}
if (OGLExtension.HasTextureMirrorClamp())
if (OGLExtension.TextureMirrorClamp)
{
switch (Wrap)
{
@ -245,7 +250,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
throw new ArgumentException(nameof(Wrap));
throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!");
}
public static TextureMinFilter GetTextureMinFilter(
@ -259,7 +264,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
throw new ArgumentException(nameof(MinFilter));
throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!");
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
@ -270,7 +275,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
throw new ArgumentException(nameof(Filter));
throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!");
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
@ -284,7 +289,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalBlendEquation.Max: return BlendEquationMode.Max;
}
throw new ArgumentException(nameof(BlendEquation));
throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!");
}
public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor)
@ -315,7 +320,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendingFactor.ConstantColor;
}
throw new ArgumentException(nameof(BlendFactor));
throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!");
}
}
}

View file

@ -1,40 +1,17 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLExtension
{
private static bool Initialized = false;
private static Lazy<bool> s_EnhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static bool EnhancedLayouts;
private static bool TextureMirrorClamp;
public static bool HasEnhancedLayouts()
{
EnsureInitialized();
return EnhancedLayouts;
}
public static bool HasTextureMirrorClamp()
{
EnsureInitialized();
return TextureMirrorClamp;
}
private static void EnsureInitialized()
{
if (Initialized)
{
return;
}
EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts");
TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp");
}
public static bool EnhancedLayouts => s_EnhancedLayouts.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
public static bool ViewportArray => s_ViewportArray.Value;
private static bool HasExtension(string Name)
{

View file

@ -25,6 +25,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, 3 }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> FloatAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float },
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float },
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat },
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Float },
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat },
{ GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat },
{ GalVertexAttribSize._32, VertexAttribPointerType.Float },
{ GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
@ -116,9 +129,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BlendFuncSrcAlpha = GalBlendFactor.One,
BlendFuncDstAlpha = GalBlendFactor.Zero,
ColorMask = ColorMaskRgba.Default,
PrimitiveRestartEnabled = false,
PrimitiveRestartIndex = 0
};
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
{
Old.ColorMasks[Index] = ColorMaskRgba.Default;
}
}
public void Bind(GalPipelineState New)
@ -164,8 +184,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (New.DepthWriteEnabled != Old.DepthWriteEnabled)
{
Rasterizer.DepthWriteEnabled = New.DepthWriteEnabled;
GL.DepthMask(New.DepthWriteEnabled);
}
@ -182,6 +200,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Enable(EnableCap.StencilTest, New.StencilTestEnabled);
}
if (New.StencilTwoSideEnabled != Old.StencilTwoSideEnabled)
{
Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled);
}
if (New.StencilTestEnabled)
{
if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc ||
@ -285,6 +308,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
{
if (!New.ColorMasks[Index].Equals(Old.ColorMasks[Index]))
{
GL.ColorMask(
Index,
New.ColorMasks[Index].Red,
New.ColorMasks[Index].Green,
New.ColorMasks[Index].Blue,
New.ColorMasks[Index].Alpha);
}
}
if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled)
{
Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled);
@ -356,8 +392,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
continue;
}
GL.EnableVertexAttribArray(Attrib.Index);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
bool Unsigned =
@ -373,35 +407,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (Attrib.Type == GalVertexAttribType.Float)
{
Type = VertexAttribPointerType.Float;
Type = GetType(FloatAttribTypes, Attrib);
}
else
{
if (Unsigned)
{
Type = UnsignedAttribTypes[Attrib.Size];
Type = GetType(UnsignedAttribTypes, Attrib);
}
else
{
Type = SignedAttribTypes[Attrib.Size];
Type = GetType(SignedAttribTypes, Attrib);
}
}
int Size = AttribElements[Attrib.Size];
if (!AttribElements.TryGetValue(Attrib.Size, out int Size))
{
throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!");
}
int Offset = Attrib.Offset;
if (Attrib.Type == GalVertexAttribType.Sint ||
Attrib.Type == GalVertexAttribType.Uint)
if (Binding.Stride != 0)
{
IntPtr Pointer = new IntPtr(Offset);
GL.EnableVertexAttribArray(Attrib.Index);
VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
if (Attrib.Type == GalVertexAttribType.Sint ||
Attrib.Type == GalVertexAttribType.Uint)
{
IntPtr Pointer = new IntPtr(Offset);
GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
}
else
{
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
}
}
else
{
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
GL.DisableVertexAttribArray(Attrib.Index);
SetConstAttrib(Attrib);
}
if (Binding.Instanced && Binding.Divisor != 0)
@ -416,6 +465,149 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> Dict, GalVertexAttrib Attrib)
{
if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type))
{
throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
}
return Type;
}
private unsafe static void SetConstAttrib(GalVertexAttrib Attrib)
{
void Unsupported()
{
throw new NotImplementedException("Constant attribute " + Attrib.Size + " not implemented!");
}
if (Attrib.Size == GalVertexAttribSize._10_10_10_2 ||
Attrib.Size == GalVertexAttribSize._11_11_10)
{
Unsupported();
}
if (Attrib.Type == GalVertexAttribType.Unorm)
{
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Snorm)
{
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Uint)
{
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Sint)
{
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Float)
{
switch (Attrib.Size)
{
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
break;
default: Unsupported(); break;
}
}
}
private void Enable(EnableCap Cap, bool Enabled)
{
if (Enabled)
@ -427,5 +619,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Disable(Cap);
}
}
public void ResetDepthMask()
{
Old.DepthWriteEnabled = true;
}
public void ResetColorMask(int Index)
{
Old.ColorMasks[Index] = ColorMaskRgba.Default;
}
}
}

View file

@ -5,8 +5,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRasterizer : IGalRasterizer
{
public bool DepthWriteEnabled { set; private get; }
private int[] VertexBuffers;
private OGLCachedResource<int> VboCache;
@ -30,8 +28,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
IboCache = new OGLCachedResource<int>(GL.DeleteBuffer);
IndexBuffer = new IbInfo();
DepthWriteEnabled = true;
}
public void LockCaches()
@ -49,17 +45,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Red,
float Green,
float Blue,
float Alpha,
float Depth,
int Stencil)
{
//OpenGL needs glDepthMask to be enabled to clear it
if (!DepthWriteEnabled)
{
GL.DepthMask(true);
}
GL.ColorMask(
Attachment,
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
@ -67,6 +61,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha });
GL.ColorMask(Attachment, true, true, true, true);
GL.DepthMask(true);
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth);
@ -76,13 +73,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil);
}
GL.ColorMask(true, true, true, true);
if (!DepthWriteEnabled)
{
GL.DepthMask(false);
}
}
public bool IsVboCached(long Key, long DataSize)
@ -119,6 +109,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long Key, int DataSize, byte[] Buffer)
{
int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
}
public void SetIndexArray(int Size, GalIndexFormat Format)
{
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
@ -135,7 +137,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return;
}
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
if (PrimType == GalPrimitiveType.Quads)
{
for (int Offset = 0; Offset < Count; Offset += 4)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else if (PrimType == GalPrimitiveType.QuadStrip)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First, 4);
for (int Offset = 2; Offset < Count; Offset += 2)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else
{
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
}
}
public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)

View file

@ -22,16 +22,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
private class FrameBufferAttachments
{
public long[] Colors;
public long Zeta;
public int MapCount;
public DrawBuffersEnum[] Map;
public FrameBufferAttachments()
{
Colors = new long[RenderTargetsCount];
Map = new DrawBuffersEnum[RenderTargetsCount];
}
public void SetAndClear(FrameBufferAttachments Source)
{
Zeta = Source.Zeta;
MapCount = Source.MapCount;
Source.Zeta = 0;
Source.MapCount = 0;
for (int i = 0; i < RenderTargetsCount; i++)
{
Colors[i] = Source.Colors[i];
Map[i] = Source.Map[i];
Source.Colors[i] = 0;
Source.Map[i] = 0;
}
}
}
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount;
private OGLTexture Texture;
private ImageHandler ReadTex;
private Rect Viewport;
private float[] Viewports;
private Rect Window;
private bool FlipX;
@ -50,138 +84,165 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private int SrcFb;
private int DstFb;
//Holds current attachments, used to avoid unnecesary calls to OpenGL
private int[] ColorAttachments;
private int DepthAttachment;
private int StencilAttachment;
private FrameBufferAttachments Attachments;
private FrameBufferAttachments OldAttachments;
private int CopyPBO;
public OGLRenderTarget(OGLTexture Texture)
{
ColorAttachments = new int[8];
Attachments = new FrameBufferAttachments();
OldAttachments = new FrameBufferAttachments();
Viewports = new float[RenderTargetsCount * 4];
this.Texture = Texture;
}
public void BindColor(long Key, int Attachment, GalImage Image)
public void Bind()
{
if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
if (DummyFrameBuffer == 0)
{
EnsureFrameBuffer();
Attach(ref ColorAttachments[Attachment], CachedImage.Handle, FramebufferAttachment.ColorAttachment0 + Attachment);
DummyFrameBuffer = GL.GenFramebuffer();
}
else
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
ImageHandler CachedImage;
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
UnbindColor(Attachment);
}
}
long Key = Attachments.Colors[Attachment];
public void UnbindColor(int Attachment)
{
EnsureFrameBuffer();
Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment);
}
public void BindZeta(long Key, GalImage Image)
{
if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
{
EnsureFrameBuffer();
if (CachedImage.HasDepth && CachedImage.HasStencil)
if (Key == OldAttachments.Colors[Attachment])
{
if (DepthAttachment != CachedImage.Handle ||
StencilAttachment != CachedImage.Handle)
continue;
}
int Handle = 0;
if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage))
{
Handle = CachedImage.Handle;
}
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Handle,
0);
}
if (Attachments.Zeta != OldAttachments.Zeta)
{
if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage))
{
if (CachedImage.HasDepth && CachedImage.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
CachedImage.Handle,
0);
DepthAttachment = CachedImage.Handle;
StencilAttachment = CachedImage.Handle;
}
}
else if (CachedImage.HasDepth)
{
Attach(ref DepthAttachment, CachedImage.Handle, FramebufferAttachment.DepthAttachment);
else if (CachedImage.HasDepth)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
CachedImage.Handle,
0);
Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment);
}
else if (CachedImage.HasStencil)
{
Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment);
Attach(ref StencilAttachment, CachedImage.Handle, FramebufferAttachment.StencilAttachment);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else
{
throw new NotImplementedException();
}
}
else
{
throw new InvalidOperationException();
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
}
}
if (OGLExtension.ViewportArray)
{
GL.ViewportArray(0, RenderTargetsCount, Viewports);
}
else
{
UnbindZeta();
GL.Viewport(
(int)Viewports[0],
(int)Viewports[1],
(int)Viewports[2],
(int)Viewports[3]);
}
if (Attachments.MapCount > 1)
{
GL.DrawBuffers(Attachments.MapCount, Attachments.Map);
}
else if (Attachments.MapCount == 1)
{
GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]);
}
else
{
GL.DrawBuffer(DrawBufferMode.None);
}
OldAttachments.SetAndClear(Attachments);
}
private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment)
public void BindColor(long Key, int Attachment)
{
if (OldHandle != NewHandle)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FbAttachment,
NewHandle,
0);
Attachments.Colors[Attachment] = Key;
}
OldHandle = NewHandle;
}
public void UnbindColor(int Attachment)
{
Attachments.Colors[Attachment] = 0;
}
public void BindZeta(long Key)
{
Attachments.Zeta = Key;
}
public void UnbindZeta()
{
EnsureFrameBuffer();
if (DepthAttachment != 0 || StencilAttachment != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
DepthAttachment = 0;
StencilAttachment = 0;
}
Attachments.Zeta = 0;
}
public void Set(long Key)
public void Present(long Key)
{
Texture.TryGetImageHandler(Key, out ReadTex);
}
public void SetMap(int[] Map)
{
if (Map != null && Map.Length > 0)
if (Map != null)
{
DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length];
Attachments.MapCount = Map.Length;
for (int i = 0; i < Map.Length; i++)
for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++)
{
Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i];
Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment];
}
GL.DrawBuffers(Mode.Length, Mode);
}
else
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Attachments.MapCount = 0;
}
}
@ -201,20 +262,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Window = new Rect(0, 0, Width, Height);
}
public void SetViewport(int X, int Y, int Width, int Height)
public void SetViewport(int Attachment, int X, int Y, int Width, int Height)
{
Viewport = new Rect(X, Y, Width, Height);
int Offset = Attachment * 4;
SetViewport(Viewport);
}
private void SetViewport(Rect Viewport)
{
GL.Viewport(
Viewport.X,
Viewport.Y,
Viewport.Width,
Viewport.Height);
Viewports[Offset + 0] = X;
Viewports[Offset + 1] = Y;
Viewports[Offset + 2] = Width;
Viewports[Offset + 3] = Height;
}
public void Render()
@ -276,7 +331,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
@ -285,8 +339,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
DstX0, DstY0, DstX1, DstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
EnsureFrameBuffer();
}
public void Copy(
@ -343,8 +395,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Clear(Mask);
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
EnsureFrameBuffer();
}
}
@ -419,15 +469,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL
(CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
(CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
}
private void EnsureFrameBuffer()
{
if (DummyFrameBuffer == 0)
{
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
}
}
}

View file

@ -55,16 +55,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GlslDecompiler Decompiler = new GlslDecompiler();
int ShaderDumpIndex = ShaderDumper.DumpIndex;
if (IsDualVp)
{
ShaderDumper.Dump(Memory, Position, Type, "a");
ShaderDumper.Dump(Memory, PositionB, Type, "b");
Program = Decompiler.Decompile(
Memory,
Position,
PositionB,
Type);
Program = Decompiler.Decompile(Memory, Position, PositionB, Type);
}
else
{
@ -73,11 +71,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Program = Decompiler.Decompile(Memory, Position, Type);
}
return new OGLShaderStage(
Type,
Program.Code,
Program.Uniforms,
Program.Textures);
string Code = Program.Code;
if (ShaderDumper.IsDumpEnabled())
{
Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code;
}
return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures);
}
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
@ -133,7 +134,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
//Enhanced layouts are required for Geometry shaders
//skip this stage if current driver has no ARB_enhanced_layouts
if (!OGLExtension.HasEnhancedLayouts())
if (!OGLExtension.EnhancedLayouts)
{
return;
}

View file

@ -39,41 +39,25 @@ namespace Ryujinx.Graphics.Gal.OpenGL
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size);
GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask;
bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END;
if (ImageUtils.IsCompressed(Image.Format) && !IsASTC)
if (ImageUtils.IsCompressed(Image.Format))
{
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
GL.CompressedTexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Size,
IntPtr.Zero);
throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
}
else
{
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
IntPtr.Zero);
}
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
IntPtr.Zero);
}
public void Create(long Key, byte[] Data, GalImage Image)
@ -87,11 +71,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask;
bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END;
if (ImageUtils.IsCompressed(Image.Format) && !IsASTC)
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
{
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
@ -108,7 +88,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
else
{
//TODO: Use KHR_texture_compression_astc_hdr when available
if (IsASTC)
if (IsAstc(Image.Format))
{
int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format);
int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
@ -120,17 +100,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Image.Width,
Image.Height, 1);
Image.Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
}
else if (TypeLess == GalImageFormat.G8R8)
{
Data = ImageConverter.G8R8ToR8G8(
Data,
Image.Width,
Image.Height,
1);
Image.Format = GalImageFormat.R8G8 | (Image.Format & GalImageFormat.TypeMask);
Image.Format = GalImageFormat.RGBA8 | GalImageFormat.Unorm;
}
(PixelInternalFormat InternalFmt,
@ -150,6 +120,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
private static bool IsAstc(GalImageFormat Format)
{
Format &= GalImageFormat.FormatMask;
return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd;
}
public bool TryGetImage(long Key, out GalImage Image)
{
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))

View file

@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclSsy()
{
SB.AppendLine("uint " + GlslDecl.SsyCursorName + "= 0;");
SB.AppendLine("uint " + GlslDecl.SsyCursorName + " = 0;");
SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine);
}

View file

@ -585,6 +585,7 @@ namespace Ryujinx.Graphics.Gal.Shader
bool AbsA = OpCode.Read(46);
bool NegA = OpCode.Read(48);
bool AbsB = OpCode.Read(49);
bool Sat = OpCode.Read(50);
ShaderIrNode OperA = OpCode.Gpr8(), OperB;
@ -603,12 +604,13 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat))));
}
private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool NegB = OpCode.Read(48);
bool Sat = OpCode.Read(50);
ShaderIrNode OperA = OpCode.Gpr8(), OperB;
@ -625,13 +627,14 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat))));
}
private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool NegB = OpCode.Read(48);
bool NegC = OpCode.Read(49);
bool Sat = OpCode.Read(50);
ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;
@ -658,7 +661,7 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat))));
}
private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)

View file

@ -2,6 +2,9 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecodeHelper
{
private static readonly ShaderIrOperImmf ImmfZero = new ShaderIrOperImmf(0);
private static readonly ShaderIrOperImmf ImmfOne = new ShaderIrOperImmf(1);
public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg)
{
return GetAluFneg(GetAluFabs(Node, Abs), Neg);
@ -17,6 +20,11 @@ namespace Ryujinx.Graphics.Gal.Shader
return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node;
}
public static ShaderIrNode GetAluFsat(ShaderIrNode Node, bool Sat)
{
return Sat ? new ShaderIrOp(ShaderIrInst.Fclamp, Node, ImmfZero, ImmfOne) : Node;
}
public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg)
{
return GetAluIneg(GetAluIabs(Node, Abs), Neg);

View file

@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value;
Current.Branch = Enqueue(Target, Current);
Enqueue(Target, Current);
}
}
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Gal.Shader
DbgOpCode += (Decode?.Method.Name ?? "???");
if (Decode == ShaderDecode.Bra)
if (Decode == ShaderDecode.Bra || Decode == ShaderDecode.Ssy)
{
int Offset = ((int)(OpCode >> 20) << 8) >> 8;

View file

@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal
{
private static string RuntimeDir;
private static int DumpIndex = 1;
public static int DumpIndex { get; private set; } = 1;
public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "")
{
if (string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath))
if (!IsDumpEnabled())
{
return;
}
@ -25,9 +25,10 @@ namespace Ryujinx.Graphics.Gal
using (FileStream FullFile = File.Create(FullPath))
using (FileStream CodeFile = File.Create(CodePath))
using (BinaryWriter FullWriter = new BinaryWriter(FullFile))
using (BinaryWriter CodeWriter = new BinaryWriter(CodeFile))
{
BinaryWriter FullWriter = new BinaryWriter(FullFile);
BinaryWriter CodeWriter = new BinaryWriter(CodeFile);
for (long i = 0; i < 0x50; i += 4)
{
FullWriter.Write(Memory.ReadInt32(Position + i));
@ -69,6 +70,11 @@ namespace Ryujinx.Graphics.Gal
}
}
public static bool IsDumpEnabled()
{
return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath);
}
private static string FullDir()
{
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));

View file

@ -46,7 +46,7 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
}
Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage);
Gpu.Renderer.RenderTarget.BindColor(Position, Attachment);
}
public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage)
@ -60,7 +60,7 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
}
Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage);
Gpu.Renderer.RenderTarget.BindZeta(Position);
}
public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1)

View file

@ -97,10 +97,14 @@ namespace Ryujinx.Graphics
SetCullFace(State);
SetDepth(State);
SetStencil(State);
SetAlphaBlending(State);
SetBlending(State);
SetColorMask(State);
SetPrimitiveRestart(State);
SetFrameBuffer(Vmm, 0);
for (int FbIndex = 0; FbIndex < 8; FbIndex++)
{
SetFrameBuffer(Vmm, FbIndex);
}
SetZeta(Vmm);
@ -137,7 +141,7 @@ namespace Ryujinx.Graphics
{
int Arg0 = PBEntry.Arguments[0];
int FbIndex = (Arg0 >> 6) & 0xf;
int Attachment = (Arg0 >> 6) & 0xf;
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
@ -150,16 +154,18 @@ namespace Ryujinx.Graphics
int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil);
SetFrameBuffer(Vmm, FbIndex);
SetFrameBuffer(Vmm, Attachment);
SetZeta(Vmm);
Gpu.Renderer.Rasterizer.ClearBuffers(
Flags,
FbIndex,
Red, Green, Blue, Alpha,
Depth,
Stencil);
SetRenderTargets();
Gpu.Renderer.RenderTarget.Bind();
Gpu.Renderer.Rasterizer.ClearBuffers(Flags, Attachment, Red, Green, Blue, Alpha, Depth, Stencil);
Gpu.Renderer.Pipeline.ResetDepthMask();
Gpu.Renderer.Pipeline.ResetColorMask(Attachment);
}
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex)
@ -204,7 +210,7 @@ namespace Ryujinx.Graphics
Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH);
Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH);
}
private void SetFrameBuffer(GalPipelineState State)
@ -337,13 +343,8 @@ namespace Ryujinx.Graphics
{
switch (FrontFace)
{
case GalFrontFace.CW:
FrontFace = GalFrontFace.CCW;
break;
case GalFrontFace.CCW:
FrontFace = GalFrontFace.CW;
break;
case GalFrontFace.CW: FrontFace = GalFrontFace.CCW; break;
case GalFrontFace.CCW: FrontFace = GalFrontFace.CW; break;
}
}
@ -396,7 +397,7 @@ namespace Ryujinx.Graphics
}
}
private void SetAlphaBlending(GalPipelineState State)
private void SetBlending(GalPipelineState State)
{
//TODO: Support independent blend properly.
State.BlendEnabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable);
@ -414,6 +415,26 @@ namespace Ryujinx.Graphics
}
}
private void SetColorMask(GalPipelineState State)
{
int ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMask);
State.ColorMask.Red = ((ColorMask >> 0) & 0xf) != 0;
State.ColorMask.Green = ((ColorMask >> 4) & 0xf) != 0;
State.ColorMask.Blue = ((ColorMask >> 8) & 0xf) != 0;
State.ColorMask.Alpha = ((ColorMask >> 12) & 0xf) != 0;
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
{
ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + Index);
State.ColorMasks[Index].Red = ((ColorMask >> 0) & 0xf) != 0;
State.ColorMasks[Index].Green = ((ColorMask >> 4) & 0xf) != 0;
State.ColorMasks[Index].Blue = ((ColorMask >> 8) & 0xf) != 0;
State.ColorMasks[Index].Alpha = ((ColorMask >> 12) & 0xf) != 0;
}
}
private void SetPrimitiveRestart(GalPipelineState State)
{
State.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable);
@ -426,21 +447,22 @@ namespace Ryujinx.Graphics
private void SetRenderTargets()
{
bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
//Commercial games do not seem to
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
if (SeparateFragData)
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
uint Count = Control & 0xf;
if (Count > 0)
{
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
uint Count = Control & 0xf;
int[] Map = new int[Count];
for (int i = 0; i < Count; i++)
for (int Index = 0; Index < Count; Index++)
{
int Shift = 4 + i * 3;
int Shift = 4 + Index * 3;
Map[i] = (int)((Control >> Shift) & 7);
Map[Index] = (int)((Control >> Shift) & 7);
}
Gpu.Renderer.RenderTarget.SetMap(Map);
@ -560,12 +582,15 @@ namespace Ryujinx.Graphics
private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State)
{
long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
long IboKey = Vmm.GetPhysicalAddress(IbPosition);
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt;
@ -582,14 +607,50 @@ namespace Ryujinx.Graphics
bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize);
bool UsesLegacyQuads =
PrimType == GalPrimitiveType.Quads ||
PrimType == GalPrimitiveType.QuadStrip;
if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index))
{
IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize);
if (!UsesLegacyQuads)
{
IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
}
else
{
byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize);
if (PrimType == GalPrimitiveType.Quads)
{
Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount);
}
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
{
Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount);
}
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer);
}
}
Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
if (!UsesLegacyQuads)
{
Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
}
else
{
if (PrimType == GalPrimitiveType.Quads)
{
Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat);
}
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
{
Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat);
}
}
}
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@ -605,10 +666,19 @@ namespace Ryujinx.Graphics
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
}
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
int Offset = (Packed >> 7) & 0x3fff;
//Note: 16 is the maximum size of an attribute,
//having a component size of 32-bits with 4 elements (a vec4).
IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16);
Attribs[ArrayIndex].Add(new GalVertexAttrib(
Attr,
((Packed >> 6) & 0x1) != 0,
(Packed >> 7) & 0x3fff,
Offset,
Pointer,
(GalVertexAttribSize)((Packed >> 21) & 0x3f),
(GalVertexAttribType)((Packed >> 27) & 0x7),
((Packed >> 31) & 0x1) != 0));
@ -702,6 +772,8 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Pipeline.Bind(State);
Gpu.Renderer.RenderTarget.Bind();
if (IndexCount != 0)
{
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
@ -712,6 +784,27 @@ namespace Ryujinx.Graphics
long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
//Quad primitive types were deprecated on OpenGL 3.x,
//they are converted to a triangles index buffer on IB creation,
//so we should use the triangles type here too.
if (PrimType == GalPrimitiveType.Quads ||
PrimType == GalPrimitiveType.QuadStrip)
{
PrimType = GalPrimitiveType.Triangles;
//Note: We assume that index first points to the first
//vertex of a quad, if it points to the middle of a
//quad (First % 4 != 0 for Quads) then it will not work properly.
if (PrimType == GalPrimitiveType.Quads)
{
IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst);
}
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
{
IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst);
}
}
Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType);
}
else

View file

@ -23,6 +23,7 @@ namespace Ryujinx.Graphics
StencilBackFuncRef = 0x3d5,
StencilBackMask = 0x3d6,
StencilBackFuncMask = 0x3d7,
ColorMask = 0x3e4,
RTSeparateFragData = 0x3eb,
ZetaAddress = 0x3f8,
ZetaFormat = 0x3fa,
@ -78,6 +79,7 @@ namespace Ryujinx.Graphics
CullFaceEnable = 0x646,
FrontFace = 0x647,
CullFace = 0x648,
ColorMaskN = 0x680,
QueryAddress = 0x6c0,
QuerySequence = 0x6c2,
QueryControl = 0x6c3,

View file

@ -0,0 +1,81 @@
using System;
namespace Ryujinx.Graphics
{
static class QuadHelper
{
public static int ConvertIbSizeQuadsToTris(int Size)
{
return Size <= 0 ? 0 : (Size / 4) * 6;
}
public static int ConvertIbSizeQuadStripToTris(int Size)
{
return Size <= 1 ? 0 : ((Size - 2) / 2) * 6;
}
public static byte[] ConvertIbQuadsToTris(byte[] Data, int EntrySize, int Count)
{
int PrimitivesCount = Count / 4;
int QuadPrimSize = 4 * EntrySize;
int TrisPrimSize = 6 * EntrySize;
byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
for (int Prim = 0; Prim < PrimitivesCount; Prim++)
{
void AssignIndex(int Src, int Dst, int CopyCount = 1)
{
Src = Prim * QuadPrimSize + Src * EntrySize;
Dst = Prim * TrisPrimSize + Dst * EntrySize;
Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
}
//0 1 2 -> 0 1 2.
AssignIndex(0, 0, 3);
//2 3 -> 3 4.
AssignIndex(2, 3, 2);
//0 -> 5.
AssignIndex(0, 5);
}
return Output;
}
public static byte[] ConvertIbQuadStripToTris(byte[] Data, int EntrySize, int Count)
{
int PrimitivesCount = (Count - 2) / 2;
int QuadPrimSize = 2 * EntrySize;
int TrisPrimSize = 6 * EntrySize;
byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
for (int Prim = 0; Prim < PrimitivesCount; Prim++)
{
void AssignIndex(int Src, int Dst, int CopyCount = 1)
{
Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize;
Dst = Prim * TrisPrimSize + Dst * EntrySize;
Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
}
//-2 -1 0 -> 0 1 2.
AssignIndex(-2, 0, 3);
//0 1 -> 3 4.
AssignIndex(0, 3, 2);
//-2 -> 5.
AssignIndex(-2, 5);
}
return Output;
}
}
}

View file

@ -19,6 +19,7 @@
<ItemGroup>
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project>

View file

@ -1,24 +0,0 @@
namespace Ryujinx.Graphics.Texture
{
static class ImageConverter
{
public static byte[] G8R8ToR8G8(
byte[] Data,
int Width,
int Height,
int Depth)
{
int Texels = Width * Height * Depth;
byte[] Output = new byte[Texels * 2];
for (int Texel = 0; Texel < Texels; Texel++)
{
Output[Texel * 2 + 0] = Data[Texel * 2 + 1];
Output[Texel * 2 + 1] = Data[Texel * 2 + 0];
}
return Output;
}
}
}

View file

@ -35,105 +35,105 @@ namespace Ryujinx.Graphics.Texture
}
}
private const GalImageFormat Snorm = GalImageFormat.Snorm;
private const GalImageFormat Unorm = GalImageFormat.Unorm;
private const GalImageFormat Sint = GalImageFormat.Sint;
private const GalImageFormat Uint = GalImageFormat.Uint;
private const GalImageFormat Sfloat = GalImageFormat.Sfloat;
private const GalImageFormat Snorm = GalImageFormat.Snorm;
private const GalImageFormat Unorm = GalImageFormat.Unorm;
private const GalImageFormat Sint = GalImageFormat.Sint;
private const GalImageFormat Uint = GalImageFormat.Uint;
private const GalImageFormat Float = GalImageFormat.Float;
private const GalImageFormat Srgb = GalImageFormat.Srgb;
private static readonly Dictionary<GalTextureFormat, GalImageFormat> s_TextureTable =
new Dictionary<GalTextureFormat, GalImageFormat>()
{
{ GalTextureFormat.R32G32B32A32, GalImageFormat.R32G32B32A32 | Sint | Uint | Sfloat },
{ GalTextureFormat.R16G16B16A16, GalImageFormat.R16G16B16A16 | Snorm | Unorm | Sint | Uint | Sfloat },
{ GalTextureFormat.R32G32, GalImageFormat.R32G32 | Sint | Uint | Sfloat },
{ GalTextureFormat.A8B8G8R8, GalImageFormat.A8B8G8R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.A2B10G10R10, GalImageFormat.A2B10G10R10 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.G8R8, GalImageFormat.G8R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Sfloat },
{ GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.R16G16, GalImageFormat.R16G16 | Snorm | Sfloat },
{ GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Sfloat },
{ GalTextureFormat.A4B4G4R4, GalImageFormat.A4B4G4R4 | Unorm },
{ GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm },
{ GalTextureFormat.B5G6R5, GalImageFormat.B5G6R5 | Unorm },
{ GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11 | Sfloat },
{ GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm },
{ GalTextureFormat.ZF32, GalImageFormat.D32 | Sfloat },
{ GalTextureFormat.ZF32_X24S8, GalImageFormat.D32_S8 | Unorm },
{ GalTextureFormat.Z16, GalImageFormat.D16 | Unorm },
{
{ GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float },
{ GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float },
{ GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float },
{ GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb },
{ GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float },
{ GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Float },
{ GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float },
{ GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm },
{ GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm },
{ GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm },
{ GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float },
{ GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint },
{ GalTextureFormat.D32F, GalImageFormat.D32 | Float },
{ GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Unorm },
{ GalTextureFormat.D16, GalImageFormat.D16 | Unorm },
//Compressed formats
{ GalTextureFormat.BC6H_SF16, GalImageFormat.BC6H_SF16 | Unorm },
{ GalTextureFormat.BC6H_UF16, GalImageFormat.BC6H_UF16 | Sfloat },
{ GalTextureFormat.BC7U, GalImageFormat.BC7 | Unorm },
{ GalTextureFormat.BC1, GalImageFormat.BC1_RGBA | Unorm },
{ GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm },
{ GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm },
{ GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm },
{ GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm },
{ GalTextureFormat.Astc2D4x4, GalImageFormat.ASTC_4x4 | Unorm },
{ GalTextureFormat.Astc2D5x5, GalImageFormat.ASTC_5x5 | Unorm },
{ GalTextureFormat.Astc2D6x6, GalImageFormat.ASTC_6x6 | Unorm },
{ GalTextureFormat.Astc2D8x8, GalImageFormat.ASTC_8x8 | Unorm },
{ GalTextureFormat.Astc2D10x10, GalImageFormat.ASTC_10x10 | Unorm },
{ GalTextureFormat.Astc2D12x12, GalImageFormat.ASTC_12x12 | Unorm },
{ GalTextureFormat.Astc2D5x4, GalImageFormat.ASTC_5x4 | Unorm },
{ GalTextureFormat.Astc2D6x5, GalImageFormat.ASTC_6x5 | Unorm },
{ GalTextureFormat.Astc2D8x6, GalImageFormat.ASTC_8x6 | Unorm },
{ GalTextureFormat.Astc2D10x8, GalImageFormat.ASTC_10x8 | Unorm },
{ GalTextureFormat.Astc2D12x10, GalImageFormat.ASTC_12x10 | Unorm },
{ GalTextureFormat.Astc2D8x5, GalImageFormat.ASTC_8x5 | Unorm },
{ GalTextureFormat.Astc2D10x5, GalImageFormat.ASTC_10x5 | Unorm },
{ GalTextureFormat.Astc2D10x6, GalImageFormat.ASTC_10x6 | Unorm }
};
//Compressed formats
{ GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float },
{ GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float },
{ GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb },
{ GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb },
{ GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb },
{ GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb },
{ GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm },
{ GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm },
{ GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb },
{ GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb },
{ GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb },
{ GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb },
{ GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb },
{ GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb },
{ GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb },
{ GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb },
{ GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb },
{ GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb },
{ GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb },
{ GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb },
{ GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb },
{ GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb }
};
private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
new Dictionary<GalImageFormat, ImageDescriptor>()
{
{ GalImageFormat.R32G32B32A32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R16G16B16A16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R32G32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.A8B8G8R8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.A2B10G10R10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.A4B4G4R4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BC6H_SF16, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC6H_UF16, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.A1R5G5B5, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.B5G6R5, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BC7, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.R16G16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R8G8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.G8R8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.B10G11R11, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BC1_RGBA, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.ASTC_4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.ASTC_5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) },
{ GalImageFormat.ASTC_6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) },
{ GalImageFormat.ASTC_8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) },
{ GalImageFormat.ASTC_10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
{ GalImageFormat.ASTC_12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
{ GalImageFormat.ASTC_5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) },
{ GalImageFormat.ASTC_6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) },
{ GalImageFormat.ASTC_8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) },
{ GalImageFormat.ASTC_10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) },
{ GalImageFormat.ASTC_12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
{ GalImageFormat.ASTC_8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) },
{ GalImageFormat.ASTC_10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) },
{ GalImageFormat.ASTC_10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) },
{ GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
{ GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) },
{ GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
{ GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
{ GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) },
{ GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) },
{ GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
{ GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) },
{ GalImageFormat.D24_S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
{ GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D32_S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) },
{ GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
{ GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }
};
public static GalImageFormat ConvertTexture(
@ -141,60 +141,62 @@ namespace Ryujinx.Graphics.Texture
GalTextureType RType,
GalTextureType GType,
GalTextureType BType,
GalTextureType AType)
GalTextureType AType,
bool ConvSrgb)
{
if (RType != GType || RType != BType || RType != AType)
{
throw new NotImplementedException("Per component types are not implemented");
throw new NotImplementedException("Per component types are not implemented!");
}
if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat))
{
throw new NotImplementedException("Texture with format " + ((int)Format).ToString("x2") + " not implemented");
throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!");
}
GalTextureType Type = RType;
GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType);
GalImageFormat FormatType = GetFormatType(RType);
GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType;
if (ImageFormat.HasFlag(FormatType))
if (!ImageFormat.HasFlag(FormatType))
{
return (ImageFormat & GalImageFormat.FormatMask) | FormatType;
}
else
{
throw new NotImplementedException("Texture with format " + Format +
" and component type " + Type + " is not implemented");
throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!");
}
return CombinedFormat;
}
public static GalImageFormat ConvertSurface(GalSurfaceFormat Format)
{
switch (Format)
{
case GalSurfaceFormat.RGBA32Float: return GalImageFormat.R32G32B32A32 | Sfloat;
case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.R32G32B32A32 | Uint;
case GalSurfaceFormat.RGBA16Float: return GalImageFormat.R16G16B16A16 | Sfloat;
case GalSurfaceFormat.RG32Float: return GalImageFormat.R32G32 | Sfloat;
case GalSurfaceFormat.RG32Sint: return GalImageFormat.R32G32 | Sint;
case GalSurfaceFormat.RG32Uint: return GalImageFormat.R32G32 | Uint;
case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.R8G8B8A8 | Unorm; //Is this right?
case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; //This one might be wrong
case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10 | Unorm;
case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8 | Unorm;
case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB;
case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8 | Snorm;
case GalSurfaceFormat.RG16Snorm: return GalImageFormat.R16G16 | Snorm;
case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat;
case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat;
case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat;
case GalSurfaceFormat.RG8Unorm: return GalImageFormat.R8G8 | Unorm;
case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8 | Snorm;
case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat;
case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm;
case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm;
case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.B5G6R5 | Unorm;
case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.A1R5G5B5 | Unorm;
case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float;
case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint;
case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float;
case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float;
case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint;
case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint;
case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm;
case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb;
case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm;
case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm;
case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb;
case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm;
case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm;
case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm;
case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float;
case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float;
case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float;
case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint;
case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm;
case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm;
case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float;
case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm;
case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint;
case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm;
case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint;
case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm;
case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm;
}
throw new NotImplementedException(Format.ToString());
@ -204,11 +206,11 @@ namespace Ryujinx.Graphics.Texture
{
switch (Format)
{
case GalZetaFormat.Z32Float: return GalImageFormat.D32 | Sfloat;
case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_S8 | Unorm;
case GalZetaFormat.Z16Unorm: return GalImageFormat.D16 | Unorm;
case GalZetaFormat.Z24S8Unorm: return GalImageFormat.D24_S8 | Unorm;
case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Sfloat;
case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float;
case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm;
case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm;
case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm;
case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float;
}
throw new NotImplementedException(Format.ToString());
@ -235,18 +237,23 @@ namespace Ryujinx.Graphics.Texture
int BytesPerPixel = Desc.BytesPerPixel;
int OutOffs = 0;
//Note: Each row of the texture needs to be aligned to 4 bytes.
int Pitch = (Width * BytesPerPixel + 3) & ~3;
byte[] Data = new byte[Width * Height * BytesPerPixel];
byte[] Data = new byte[Height * Pitch];
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int OutOffs = Y * Pitch;
CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
OutOffs += BytesPerPixel;
CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
OutOffs += BytesPerPixel;
}
}
return Data;
@ -289,7 +296,11 @@ namespace Ryujinx.Graphics.Texture
{
ImageDescriptor Desc = GetImageDescriptor(Format);
return Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
Pitch = (Pitch + 0x1f) & ~0x1f;
return Pitch;
}
public static int GetBlockWidth(GalImageFormat Format)
@ -362,14 +373,14 @@ namespace Ryujinx.Graphics.Texture
private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
{
GalImageFormat TypeLess = (Format & GalImageFormat.FormatMask);
GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask;
if (s_ImageTable.TryGetValue(TypeLess, out ImageDescriptor Descriptor))
if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor))
{
return Descriptor;
}
throw new NotImplementedException("Image with format " + TypeLess.ToString() + " not implemented");
throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!");
}
private static GalImageFormat GetFormatType(GalTextureType Type)
@ -380,7 +391,7 @@ namespace Ryujinx.Graphics.Texture
case GalTextureType.Unorm: return Unorm;
case GalTextureType.Sint: return Sint;
case GalTextureType.Uint: return Uint;
case GalTextureType.Float: return Sfloat;
case GalTextureType.Float: return Float;
default: throw new NotImplementedException(((int)Type).ToString());
}

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Texture
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
return new GalImage(
GalImage Image = new GalImage(
Width,
Height,
TileWidth,
@ -51,6 +51,13 @@ namespace Ryujinx.Graphics.Texture
YSource,
ZSource,
WSource);
if (Layout == GalMemoryLayout.Pitch)
{
Image.Pitch = (Tic[3] & 0xffff) << 5;
}
return Image;
}
public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
@ -90,7 +97,9 @@ namespace Ryujinx.Graphics.Texture
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType);
bool ConvSrgb = ((Tic[4] >> 22) & 1) != 0;
return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType, ConvSrgb);
}
private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count)

View file

@ -35,9 +35,11 @@ namespace Ryujinx.HLE.FileSystem
}
}
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
string SavePath = Path.Combine(BaseSavePath,
SaveMetaData.SaveId.ToString("x16"),
SaveMetaData.UserId.ToString(),
SaveAccount,
SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty);
return SavePath;

View file

@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Utilities;
namespace Ryujinx.HLE.FileSystem
{
@ -6,7 +6,7 @@ namespace Ryujinx.HLE.FileSystem
{
public long TitleId { get; private set; }
public long SaveId { get; private set; }
public UserId UserId { get; private set; }
public UInt128 UserId { get; private set; }
public SaveDataType SaveDataType { get; private set; }
public SaveSpaceId SaveSpaceId { get; private set; }
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.FileSystem
long TitleId,
long SaveId,
SaveDataType SaveDataType,
UserId UserId,
UInt128 UserId,
SaveSpaceId SaveSpaceId)
{
this.TitleId = TitleId;

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -1,7 +1,5 @@
using System;
using System.IO;
namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class CastExpression : BaseNode
@ -24,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
To.PrintLeft(Writer);
Writer.Write(">(");
From.PrintLeft(Writer);
Writer.Write(")");
Writer.Write(")");
}
}
}

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -1,10 +1,10 @@
using LibHac;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Font;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -51,6 +51,8 @@ namespace Ryujinx.HLE.HOS
public string CurrentTitle { get; private set; }
public bool EnableFsIntegrityChecks { get; set; }
public Horizon(Switch Device)
{
this.Device = Device;
@ -71,6 +73,8 @@ namespace Ryujinx.HLE.HOS
Withholders = new LinkedList<KThread>();
Scheduler.StartAutoPreemptionThread();
if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
!Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
{
@ -100,7 +104,7 @@ namespace Ryujinx.HLE.HOS
if (File.Exists(NpdmFileName))
{
Device.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open))
{
@ -109,7 +113,7 @@ namespace Ryujinx.HLE.HOS
}
else
{
Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
}
Process MainProcess = MakeProcess(MetaData);
@ -123,7 +127,7 @@ namespace Ryujinx.HLE.HOS
continue;
}
Device.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
using (FileStream Input = new FileStream(File, FileMode.Open))
{
@ -164,7 +168,7 @@ namespace Ryujinx.HLE.HOS
if (MainNca == null)
{
Device.Log.PrintError(LogClass.Loader, "Unable to load XCI");
Logger.PrintError(LogClass.Loader, "Unable to load XCI");
return;
}
@ -208,22 +212,29 @@ namespace Ryujinx.HLE.HOS
if (MainNca == null)
{
Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
}
MainNca.SetBaseNca(PatchNca);
if (ControlNca != null)
{
ReadControlData(ControlNca);
}
if (PatchNca != null)
{
PatchNca.SetBaseNca(MainNca);
return (PatchNca, ControlNca);
}
return (MainNca, ControlNca);
}
public void ReadControlData(Nca ControlNca)
{
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false));
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, EnableFsIntegrityChecks));
byte[] ControlFile = ControlRomfs.GetFile("/control.nacp");
@ -252,8 +263,7 @@ namespace Ryujinx.HLE.HOS
// Load title key from the NSP's ticket in case the user doesn't have a title key file
if (TicketFile != null)
{
// todo Change when Ticket(Stream) overload is added
Ticket Ticket = new Ticket(new BinaryReader(Nsp.OpenFile(TicketFile)));
Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile));
KeySet.TitleKeys[Ticket.RightsId] = Ticket.GetTitleKey(KeySet);
}
@ -282,33 +292,33 @@ namespace Ryujinx.HLE.HOS
return;
}
Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
}
public void LoadNca(Nca MainNca, Nca ControlNca)
{
NcaSection RomfsSection = MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
NcaSection RomfsSection = MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr);
NcaSection ExefsSection = MainNca.Sections.FirstOrDefault(x => x?.IsExefs == true);
if (ExefsSection == null)
{
Device.Log.PrintError(LogClass.Loader, "No ExeFS found in NCA");
Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
return;
}
if (RomfsSection == null)
{
Device.Log.PrintError(LogClass.Loader, "No RomFS found in NCA");
Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
}
else
{
Stream RomfsStream = MainNca.OpenSection(RomfsSection.SectionNum, false, EnableFsIntegrityChecks);
return;
Device.FileSystem.SetRomFs(RomfsStream);
}
Stream RomfsStream = MainNca.OpenSection(RomfsSection.SectionNum, false);
Device.FileSystem.SetRomFs(RomfsStream);
Stream ExefsStream = MainNca.OpenSection(ExefsSection.SectionNum, false);
Stream ExefsStream = MainNca.OpenSection(ExefsSection.SectionNum, false, EnableFsIntegrityChecks);
Pfs Exefs = new Pfs(ExefsStream);
@ -316,13 +326,13 @@ namespace Ryujinx.HLE.HOS
if (Exefs.FileExists("main.npdm"))
{
Device.Log.PrintInfo(LogClass.Loader, "Loading main.npdm...");
Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
MetaData = new Npdm(Exefs.OpenFile("main.npdm"));
}
else
{
Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
}
Process MainProcess = MakeProcess(MetaData);
@ -336,7 +346,7 @@ namespace Ryujinx.HLE.HOS
continue;
}
Device.Log.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
string Name = Path.GetFileNameWithoutExtension(File.Name);
@ -348,7 +358,7 @@ namespace Ryujinx.HLE.HOS
Nacp ReadControlData()
{
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false));
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, EnableFsIntegrityChecks));
byte[] ControlFile = ControlRomfs.GetFile("/control.nacp");
@ -466,7 +476,7 @@ namespace Ryujinx.HLE.HOS
public void SignalVsync()
{
VsyncEvent.Signal();
VsyncEvent.ReadableEvent.Signal();
}
private Process MakeProcess(Npdm MetaData = null)

View file

@ -73,7 +73,10 @@ namespace Ryujinx.HLE.HOS.Ipc
{
int Unknown = ReqReader.ReadInt32();
int Handle = Process.HandleTable.OpenHandle(Session);
if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);

View file

@ -15,6 +15,15 @@ namespace Ryujinx.HLE.HOS.Kernel
private bool KeepPreempting;
public void StartAutoPreemptionThread()
{
Thread PreemptionThread = new Thread(PreemptCurrentThread);
KeepPreempting = true;
PreemptionThread.Start();
}
public void ContextSwitch()
{
lock (CoreContexts)

View file

@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return 0;
}
KThread MutexOwner = Process.HandleTable.GetData<KThread>(OwnerHandle);
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
if (MutexOwner == null)
{
@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Kernel
MutexValue &= ~HasListenersMask;
KThread MutexOwner = Process.HandleTable.GetData<KThread>(MutexValue);
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
if (MutexOwner != null)
{

View file

@ -1,38 +1,14 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KEvent : KSynchronizationObject
class KEvent
{
private bool Signaled;
public KReadableEvent ReadableEvent { get; private set; }
public KWritableEvent WritableEvent { get; private set; }
public string Name { get; private set; }
public KEvent(Horizon System, string Name = "") : base(System)
public KEvent(Horizon System)
{
this.Name = Name;
}
public override void Signal()
{
System.CriticalSectionLock.Lock();
if (!Signaled)
{
Signaled = true;
base.Signal();
}
System.CriticalSectionLock.Unlock();
}
public void Reset()
{
Signaled = false;
}
public override bool IsSignaled()
{
return Signaled;
ReadableEvent = new KReadableEvent(System, this);
WritableEvent = new KWritableEvent(this);
}
}
}

View file

@ -0,0 +1,17 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KHandleEntry
{
public KHandleEntry Next { get; set; }
public int Index { get; private set; }
public ushort HandleId { get; set; }
public object Obj { get; set; }
public KHandleEntry(int Index)
{
this.Index = Index;
}
}
}

View file

@ -148,6 +148,92 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
public long MapProcessCodeMemory(long Dst, long Src, long Size)
{
lock (Blocks)
{
long PagesCount = Size / PageSize;
bool Success = IsUnmapped(Dst, Size);
Success &= CheckRange(
Src,
Size,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
long PA = CpuMemory.GetPhysicalAddress(Src);
InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
CpuMemory.Map(Dst, PA, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
{
lock (Blocks)
{
long PagesCount = Size / PageSize;
bool Success = CheckRange(
Dst,
Size,
MemoryState.Mask,
MemoryState.CodeStatic,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
Success &= CheckRange(
Src,
Size,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
CpuMemory.Unmap(Dst, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
{
long PagesCount = Size / PageSize;
@ -755,6 +841,18 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
public bool HleIsUnmapped(long Position, long Size)
{
bool Result = false;
lock (Blocks)
{
Result = IsUnmapped(Position, Size);
}
return Result;
}
private bool IsUnmapped(long Position, long Size)
{
return CheckRange(

View file

@ -1,34 +1,183 @@
using System.Collections.Generic;
using System;
namespace Ryujinx.HLE.HOS.Kernel
{
class KProcessHandleTable
{
private IdDictionary Handles;
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
public KProcessHandleTable()
private Horizon System;
private KHandleEntry[] Table;
private KHandleEntry TableHead;
private KHandleEntry NextFreeEntry;
private int ActiveSlotsCount;
private int Size;
private ushort IdCounter;
private object LockObj;
public KProcessHandleTable(Horizon System, int Size = 1024)
{
Handles = new IdDictionary();
this.System = System;
this.Size = Size;
IdCounter = 1;
Table = new KHandleEntry[Size];
TableHead = new KHandleEntry(0);
KHandleEntry Entry = TableHead;
for (int Index = 0; Index < Size; Index++)
{
Table[Index] = Entry;
Entry.Next = new KHandleEntry(Index + 1);
Entry = Entry.Next;
}
Table[Size - 1].Next = null;
NextFreeEntry = TableHead;
LockObj = new object();
}
public int OpenHandle(object Obj)
public KernelResult GenerateHandle(object Obj, out int Handle)
{
return Handles.Add(Obj);
Handle = 0;
lock (LockObj)
{
if (ActiveSlotsCount >= Size)
{
return KernelResult.HandleTableFull;
}
KHandleEntry Entry = NextFreeEntry;
NextFreeEntry = Entry.Next;
Entry.Obj = Obj;
Entry.HandleId = IdCounter;
ActiveSlotsCount++;
Handle = (int)((IdCounter << 15) & (uint)0xffff8000) | Entry.Index;
if ((short)(IdCounter + 1) >= 0)
{
IdCounter++;
}
else
{
IdCounter = 1;
}
}
return KernelResult.Success;
}
public T GetData<T>(int Handle)
public bool CloseHandle(int Handle)
{
return Handles.GetData<T>(Handle);
if ((Handle >> 30) != 0 ||
Handle == SelfThreadHandle ||
Handle == SelfProcessHandle)
{
return false;
}
int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
bool Result = false;
lock (LockObj)
{
if (HandleId != 0 && Index < Size)
{
KHandleEntry Entry = Table[Index];
if (Entry.Obj != null && Entry.HandleId == HandleId)
{
Entry.Obj = null;
Entry.Next = NextFreeEntry;
NextFreeEntry = Entry;
ActiveSlotsCount--;
Result = true;
}
}
}
return Result;
}
public object CloseHandle(int Handle)
public T GetObject<T>(int Handle)
{
return Handles.Delete(Handle);
int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
lock (LockObj)
{
if ((Handle >> 30) == 0 && HandleId != 0)
{
KHandleEntry Entry = Table[Index];
if (Entry.HandleId == HandleId && Entry.Obj is T Obj)
{
return Obj;
}
}
}
return default(T);
}
public ICollection<object> Clear()
public KThread GetKThread(int Handle)
{
return Handles.Clear();
if (Handle == SelfThreadHandle)
{
return System.Scheduler.GetCurrentThread();
}
else
{
return GetObject<KThread>(Handle);
}
}
public void Destroy()
{
lock (LockObj)
{
for (int Index = 0; Index < Size; Index++)
{
KHandleEntry Entry = Table[Index];
if (Entry.Obj != null)
{
if (Entry.Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
Entry.Obj = null;
Entry.Next = NextFreeEntry;
NextFreeEntry = Entry;
}
}
}
}
}
}

View file

@ -0,0 +1,62 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KReadableEvent : KSynchronizationObject
{
private KEvent Parent;
private bool Signaled;
public KReadableEvent(Horizon System, KEvent Parent) : base(System)
{
this.Parent = Parent;
}
public override void Signal()
{
System.CriticalSectionLock.Lock();
if (!Signaled)
{
Signaled = true;
base.Signal();
}
System.CriticalSectionLock.Unlock();
}
public KernelResult Clear()
{
Signaled = false;
return KernelResult.Success;
}
public KernelResult ClearIfSignaled()
{
KernelResult Result;
System.CriticalSectionLock.Lock();
if (Signaled)
{
Signaled = false;
Result = KernelResult.Success;
}
else
{
Result = KernelResult.InvalidState;
}
System.CriticalSectionLock.Unlock();
return Result;
}
public override bool IsSignaled()
{
return Signaled;
}
}
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
@ -35,12 +34,6 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CoreContexts[Core] = new KCoreContext(this, CoreManager);
}
Thread PreemptionThread = new Thread(PreemptCurrentThread);
KeepPreempting = true;
PreemptionThread.Start();
}
private void PreemptThreads()

View file

@ -0,0 +1,22 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KWritableEvent
{
private KEvent Parent;
public KWritableEvent(KEvent Parent)
{
this.Parent = Parent;
}
public void Signal()
{
Parent.ReadableEvent.Signal();
}
public KernelResult Clear()
{
return Parent.ReadableEvent.Clear();
}
}
}

View file

@ -6,6 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public const int InvalidSize = 101;
public const int InvalidAddress = 102;
public const int OutOfMemory = 104;
public const int HandleTableFull = 105;
public const int NoAccessPerm = 106;
public const int InvalidPermission = 108;
public const int InvalidMemRange = 110;

View file

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel
{
enum KernelResult
{
Success = 0,
HandleTableFull = 0xd201,
InvalidHandle = 0xe401,
InvalidState = 0xfa01
}
}

View file

@ -1,8 +1,8 @@
using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
@ -39,9 +39,6 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private const uint SelfThreadHandle = 0xffff8000;
private const uint SelfProcessHandle = 0xffff8001;
private static Random Rng;
public SvcHandler(Switch Device, Process Process)
@ -63,12 +60,13 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x0e, SvcGetThreadCoreMask },
{ 0x0f, SvcSetThreadCoreMask },
{ 0x10, SvcGetCurrentProcessorNumber },
{ 0x12, SvcClearEvent },
{ 0x11, SignalEvent64 },
{ 0x12, ClearEvent64 },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
{ 0x15, SvcCreateTransferMemory },
{ 0x16, SvcCloseHandle },
{ 0x17, SvcResetSignal },
{ 0x17, ResetSignal64 },
{ 0x18, SvcWaitSynchronization },
{ 0x19, SvcCancelSynchronization },
{ 0x1a, SvcArbitrateLock },
@ -88,7 +86,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x32, SvcSetThreadActivity },
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress },
{ 0x35, SvcSignalToAddress }
{ 0x35, SvcSignalToAddress },
{ 0x45, CreateEvent64 }
};
this.Device = Device;
@ -110,11 +109,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
Func(ThreadState);
Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
}
else
{
@ -123,17 +122,5 @@ namespace Ryujinx.HLE.HOS.Kernel
throw new NotImplementedException($"0x{e.Id:x4}");
}
}
private KThread GetThread(long Tpidr, int Handle)
{
if ((uint)Handle == SelfThreadHandle)
{
return Process.GetThread(Tpidr);
}
else
{
return Process.HandleTable.GetData<KThread>(Handle);
}
}
}
}

View file

@ -1,5 +1,5 @@
using ChocolArm64.State;
using Ryujinx.HLE.Logging;
using Ryujinx.Common.Logging;
using static Ryujinx.HLE.HOS.ErrorCode;
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((Size & 0xFFFFFFFE001FFFFF) != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
}
@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Attributes != AttributeMask ||
(Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
{
Device.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!");
Logger.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
else
{
@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Src | Dst))
{
Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
{
Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Src, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideNewMapRegion(Dst, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Src | Dst))
{
Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
{
Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Src, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideNewMapRegion(Dst, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -206,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -240,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Position + Size) <= (ulong)Position)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -269,18 +269,18 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -289,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -298,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (SharedMemory.Size != Size)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -323,7 +323,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -332,7 +332,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -341,18 +341,18 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Position + Size) <= (ulong)Position)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -361,7 +361,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -372,7 +372,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -385,7 +385,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -394,7 +394,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -403,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Position + Size) <= (ulong)Position)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -414,7 +414,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
@ -425,9 +425,9 @@ namespace Ryujinx.HLE.HOS.Kernel
KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
int Handle = Process.HandleTable.OpenHandle(TransferMemory);
KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle);
ThreadState.X0 = 0;
ThreadState.X0 = (uint)Result;
ThreadState.X1 = (ulong)Handle;
}
@ -438,7 +438,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -447,7 +447,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -456,7 +456,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Position + Size) <= (ulong)Position)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -465,7 +465,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Position, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -476,7 +476,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -489,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Position))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -498,7 +498,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!PageAligned(Size) || Size == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
@ -507,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((ulong)(Position + Size) <= (ulong)Position)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -516,7 +516,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (!InsideAddrSpace(Position, Size))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -527,7 +527,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;

View file

@ -1,9 +1,9 @@
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.Logging;
using System;
using System.Threading;
@ -22,24 +22,77 @@ namespace Ryujinx.HLE.HOS.Kernel
Device.System.ExitProcess(Process.ProcessId);
}
private void SvcClearEvent(AThreadState ThreadState)
private void SignalEvent64(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
ThreadState.X0 = (ulong)SignalEvent((int)ThreadState.X0);
}
//TODO: Implement events.
private KernelResult SignalEvent(int Handle)
{
KWritableEvent WritableEvent = Process.HandleTable.GetObject<KWritableEvent>(Handle);
ThreadState.X0 = 0;
KernelResult Result;
if (WritableEvent != null)
{
WritableEvent.Signal();
Result = KernelResult.Success;
}
else
{
Result = KernelResult.InvalidHandle;
}
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!");
}
return Result;
}
private void ClearEvent64(AThreadState ThreadState)
{
ThreadState.X0 = (ulong)ClearEvent((int)ThreadState.X0);
}
private KernelResult ClearEvent(int Handle)
{
KernelResult Result;
KWritableEvent WritableEvent = Process.HandleTable.GetObject<KWritableEvent>(Handle);
if (WritableEvent == null)
{
KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
Result = ReadableEvent?.Clear() ?? KernelResult.InvalidHandle;
}
else
{
Result = WritableEvent.Clear();
}
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!");
}
return Result;
}
private void SvcCloseHandle(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
object Obj = Process.HandleTable.CloseHandle(Handle);
object Obj = Process.HandleTable.GetObject<object>(Handle);
Process.HandleTable.CloseHandle(Handle);
if (Obj == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -60,24 +113,37 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = 0;
}
private void SvcResetSignal(AThreadState ThreadState)
private void ResetSignal64(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
ThreadState.X0 = (ulong)ResetSignal((int)ThreadState.X0);
}
KEvent Event = Process.HandleTable.GetData<KEvent>(Handle);
private KernelResult ResetSignal(int Handle)
{
KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
if (Event != null)
KernelResult Result;
//TODO: KProcess support.
if (ReadableEvent != null)
{
Event.Reset();
ThreadState.X0 = 0;
Result = ReadableEvent.ClearIfSignaled();
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid event handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
Result = KernelResult.InvalidHandle;
}
if (Result == KernelResult.InvalidState)
{
Logger.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + Result + "!");
}
else if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!");
}
return Result;
}
private void SvcGetSystemTick(AThreadState ThreadState)
@ -96,10 +162,13 @@ namespace Ryujinx.HLE.HOS.Kernel
//actually exists, return error codes otherwise.
KSession Session = new KSession(ServiceFactory.MakeService(System, Name), Name);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
ThreadState.X0 = 0;
ThreadState.X1 = Handle;
ThreadState.X1 = (uint)Handle;
}
private void SvcSendSyncRequest(AThreadState ThreadState)
@ -122,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Kernel
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
KSession Session = Process.HandleTable.GetData<KSession>(Handle);
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
if (Session != null)
{
@ -151,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -178,9 +247,17 @@ namespace Ryujinx.HLE.HOS.Kernel
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
Process.PrintStackTrace(ThreadState);
if ((Reason & (1 << 31)) == 0)
{
Process.PrintStackTrace(ThreadState);
throw new GuestBrokeExecutionException();
throw new GuestBrokeExecutionException();
}
else
{
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
Process.PrintStackTrace(ThreadState);
}
}
private void SvcOutputDebugString(AThreadState ThreadState)
@ -190,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Kernel
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
Device.Log.PrintWarning(LogClass.KernelSvc, Str);
Logger.PrintWarning(LogClass.KernelSvc, Str);
ThreadState.X0 = 0;
}
@ -206,7 +283,8 @@ namespace Ryujinx.HLE.HOS.Kernel
if (InfoType == 18 ||
InfoType == 19 ||
InfoType == 20 ||
InfoType == 21)
InfoType == 21 ||
InfoType == 22)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
@ -287,5 +365,37 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = 0;
}
private void CreateEvent64(AThreadState State)
{
KernelResult Result = CreateEvent(out int WEventHandle, out int REventHandle);
State.X0 = (ulong)Result;
State.X1 = (ulong)WEventHandle;
State.X2 = (ulong)REventHandle;
}
private KernelResult CreateEvent(out int WEventHandle, out int REventHandle)
{
KEvent Event = new KEvent(System);
KernelResult Result = Process.HandleTable.GenerateHandle(Event.WritableEvent, out WEventHandle);
if (Result == KernelResult.Success)
{
Result = Process.HandleTable.GenerateHandle(Event.ReadableEvent, out REventHandle);
if (Result != KernelResult.Success)
{
Process.HandleTable.CloseHandle(WEventHandle);
}
}
else
{
REventHandle = 0;
}
return Result;
}
}
}

View file

@ -1,5 +1,5 @@
using ChocolArm64.State;
using Ryujinx.HLE.Logging;
using Ryujinx.Common.Logging;
using static Ryujinx.HLE.HOS.ErrorCode;
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((uint)Priority > 0x3f)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else if ((uint)ProcessorId > 3)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = (int)ThreadState.X0;
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread != null)
{
@ -61,14 +61,14 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
long Timeout = (long)ThreadState.X0;
Device.Log.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + Timeout.ToString("x16"));
Logger.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + Timeout.ToString("x16"));
KThread CurrentThread = System.Scheduler.GetCurrentThread();
@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = (int)ThreadState.X1;
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
KThread Thread = Process.HandleTable.GetKThread(Handle);
if (Thread != null)
{
@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -132,17 +132,17 @@ namespace Ryujinx.HLE.HOS.Kernel
int Handle = (int)ThreadState.X0;
int Priority = (int)ThreadState.X1;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"Handle = 0x" + Handle .ToString("x8") + ", " +
"Priority = 0x" + Priority.ToString("x8"));
//TODO: NPDM check.
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
KThread Thread = Process.HandleTable.GetKThread(Handle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = (int)ThreadState.X2;
Device.Log.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8"));
Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8"));
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
KThread Thread = Process.HandleTable.GetKThread(Handle);
if (Thread != null)
{
@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -178,12 +178,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcSetThreadCoreMask(AThreadState ThreadState)
{
int ThreadHandle = (int)ThreadState.X0;
int Handle = (int)ThreadState.X0;
int PrefferedCore = (int)ThreadState.X1;
long AffinityMask = (long)ThreadState.X2;
Device.Log.PrintDebug(LogClass.KernelSvc,
"ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " +
Logger.PrintDebug(LogClass.KernelSvc,
"Handle = 0x" + Handle .ToString("x8") + ", " +
"PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
@ -202,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if ((PrefferedCore | 2) != -1)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else if ((AffinityMask & (1 << PrefferedCore)) == 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
@ -219,11 +219,11 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle);
KThread Thread = Process.HandleTable.GetKThread(Handle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -234,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = (int)ThreadState.X1;
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
KThread Thread = Process.HandleTable.GetKThread(Handle);
if (Thread != null)
{
@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -269,11 +269,11 @@ namespace Ryujinx.HLE.HOS.Kernel
int Handle = (int)ThreadState.X0;
bool Pause = (int)ThreadState.X1 == 1;
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread.Owner != Process)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -304,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
long Position = (long)ThreadState.X0;
int Handle = (int)ThreadState.X1;
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -317,7 +317,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Process.GetThread(ThreadState.Tpidr) == Thread)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);

View file

@ -1,5 +1,6 @@
using ChocolArm64.State;
using Ryujinx.HLE.Logging;
using Ryujinx.Common.Logging;
using System.Collections.Generic;
using static Ryujinx.HLE.HOS.ErrorCode;
@ -13,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int HandlesCount = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " +
"HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " +
"Timeout = 0x" + Timeout .ToString("x16"));
@ -25,33 +26,38 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
KSynchronizationObject[] SyncObjs = new KSynchronizationObject[HandlesCount];
List<KSynchronizationObject> SyncObjs = new List<KSynchronizationObject>();
for (int Index = 0; Index < HandlesCount; Index++)
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
SyncObjs[Index] = SyncObj;
if (SyncObj == null)
{
break;
}
SyncObjs.Add(SyncObj);
}
int HndIndex = (int)ThreadState.X1;
ulong High = ThreadState.X1 & (0xffffffffUL << 32);
long Result = System.Synchronization.WaitFor(SyncObjs, Timeout, ref HndIndex);
long Result = System.Synchronization.WaitFor(SyncObjs.ToArray(), Timeout, ref HndIndex);
if (Result != 0)
{
if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) ||
Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled))
{
Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
}
@ -63,13 +69,13 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int ThreadHandle = (int)ThreadState.X0;
Device.Log.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8"));
Logger.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8"));
KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
KThread Thread = Process.HandleTable.GetKThread(ThreadHandle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -87,14 +93,14 @@ namespace Ryujinx.HLE.HOS.Kernel
long MutexAddress = (long)ThreadState.X1;
int RequesterHandle = (int)ThreadState.X2;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " +
"MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
"RequesterHandle = 0x" + RequesterHandle.ToString("x8"));
if (IsPointingInsideKernel(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -103,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsAddressNotWordAligned(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -119,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -129,11 +135,11 @@ namespace Ryujinx.HLE.HOS.Kernel
{
long MutexAddress = (long)ThreadState.X0;
Device.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16"));
Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16"));
if (IsPointingInsideKernel(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -142,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsAddressNotWordAligned(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -153,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -166,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int ThreadHandle = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
"CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " +
"ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " +
@ -174,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsPointingInsideKernel(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -183,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsAddressNotWordAligned(MutexAddress))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -201,11 +207,11 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout))
{
Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
else
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
}
@ -217,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long Address = (long)ThreadState.X0;
int Count = (int)ThreadState.X1;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"Address = 0x" + Address.ToString("x16") + ", " +
"Count = 0x" + Count .ToString("x8"));
@ -233,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int Value = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"Address = 0x" + Address.ToString("x16") + ", " +
"Type = " + Type .ToString() + ", " +
"Value = 0x" + Value .ToString("x8") + ", " +
@ -241,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsPointingInsideKernel(Address))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -250,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsAddressNotWordAligned(Address))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -280,7 +286,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
@ -293,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int Value = (int)ThreadState.X2;
int Count = (int)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
Logger.PrintDebug(LogClass.KernelSvc,
"Address = 0x" + Address.ToString("x16") + ", " +
"Type = " + Type .ToString() + ", " +
"Value = 0x" + Value .ToString("x8") + ", " +
@ -301,7 +307,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsPointingInsideKernel(Address))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -310,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (IsAddressNotWordAligned(Address))
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -340,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Result != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;

View file

@ -3,6 +3,7 @@ using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
using LibHac;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
using Ryujinx.HLE.HOS.Kernel;
@ -11,7 +12,6 @@ using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Concurrent;
@ -71,7 +71,22 @@ namespace Ryujinx.HLE.HOS
TlsPages = new List<KTlsPageManager>();
HandleTable = new KProcessHandleTable();
int HandleTableSize = 1024;
if (MetaData != null)
{
foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items)
{
if (Item.HasHandleTableSize)
{
HandleTableSize = Item.HandleTableSize;
break;
}
}
}
HandleTable = new KProcessHandleTable(Device.System, HandleTableSize);
AppletState = new AppletStateMgr(Device.System);
@ -91,13 +106,37 @@ namespace Ryujinx.HLE.HOS
throw new ObjectDisposedException(nameof(Process));
}
Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
long ImageEnd = LoadProgram(Program, ImageBase);
Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase);
ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize);
}
public long LoadProgram(IExecutable Program, long ExecutableBase)
{
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}.");
Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase);
Executables.Add(Executable);
ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize);
return Executable.ImageEnd;
}
public void RemoveProgram(long ExecutableBase)
{
foreach (Executable Executable in Executables)
{
if (Executable.ImageBase == ExecutableBase)
{
Executables.Remove(Executable);
break;
}
}
}
public void SetEmptyArgs()
@ -139,7 +178,7 @@ namespace Ryujinx.HLE.HOS
return false;
}
KThread MainThread = HandleTable.GetData<KThread>(Handle);
KThread MainThread = HandleTable.GetKThread(Handle);
if (NeedsHbAbi)
{
@ -190,7 +229,7 @@ namespace Ryujinx.HLE.HOS
Thread.LastPc = EntryPoint;
int Handle = HandleTable.OpenHandle(Thread);
HandleTable.GenerateHandle(Thread, out int Handle);
CpuThread.ThreadState.CntfrqEl0 = TickFreq;
CpuThread.ThreadState.Tpidr = Tpidr;
@ -280,7 +319,7 @@ namespace Ryujinx.HLE.HOS
string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
Device.Log.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
}
private ATranslator GetTranslator()
@ -335,7 +374,7 @@ namespace Ryujinx.HLE.HOS
FramePointer = Memory.ReadInt64(FramePointer);
}
Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString());
Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
}
private bool TryGetSubName(Executable Exe, long Position, out string Name)
@ -427,13 +466,7 @@ namespace Ryujinx.HLE.HOS
Disposed = true;
foreach (object Obj in HandleTable.Clear())
{
if (Obj is KSession Session)
{
Session.Dispose();
}
}
HandleTable.Destroy();
INvDrvServices.UnloadProcess(this);
@ -442,7 +475,7 @@ namespace Ryujinx.HLE.HOS
File.Delete(Executables[0].FilePath);
}
Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
}
public void Dispose()

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
using static Ryujinx.HLE.HOS.ErrorCode;
@ -23,11 +24,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc
{ 3, ListOpenUsers },
{ 4, GetLastOpenedUser },
{ 5, GetProfile },
{ 50, IsUserRegistrationRequestPermitted },
{ 51, TrySelectUserWithoutInteraction },
{ 100, InitializeApplicationInfo },
{ 101, GetBaasAccountManagerForApplication }
};
}
// GetUserCount() -> i32
public long GetUserCount(ServiceCtx Context)
{
Context.ResponseData.Write(Context.Device.System.State.GetUserCount());
@ -35,22 +39,25 @@ namespace Ryujinx.HLE.HOS.Services.Acc
return 0;
}
// GetUserExistence(nn::account::Uid) -> bool
public long GetUserExistence(ServiceCtx Context)
{
UserId Uuid = new UserId(
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _) ? 1 : 0);
Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _));
return 0;
}
// ListAllUsers() -> array<nn::account::Uid, 0xa>
public long ListAllUsers(ServiceCtx Context)
{
return WriteUserList(Context, Context.Device.System.State.GetAllUsers());
}
// ListOpenUsers() -> array<nn::account::Uid, 0xa>
public long ListOpenUsers(ServiceCtx Context)
{
return WriteUserList(Context, Context.Device.System.State.GetOpenUsers());
@ -70,17 +77,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc
break;
}
byte[] Uuid = Profile.Uuid.Bytes;
for (int Index = Uuid.Length - 1; Index >= 0; Index--)
{
Context.Memory.WriteByte(OutputPosition + Offset++, Uuid[Index]);
}
Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.Low);
Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.High);
}
return 0;
}
// GetLastOpenedUser() -> nn::account::Uid
public long GetLastOpenedUser(ServiceCtx Context)
{
UserProfile LastOpened = Context.Device.System.State.LastOpenUser;
@ -90,15 +94,16 @@ namespace Ryujinx.HLE.HOS.Services.Acc
return 0;
}
// GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile>
public long GetProfile(ServiceCtx Context)
{
UserId Uuid = new UserId(
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
if (!Context.Device.System.State.TryGetUser(Uuid, out UserProfile Profile))
{
Context.Device.Log.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!");
Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!");
return MakeError(ErrorModule.Account, AccErr.UserNotFound);
}
@ -108,16 +113,50 @@ namespace Ryujinx.HLE.HOS.Services.Acc
return 0;
}
public long InitializeApplicationInfo(ServiceCtx Context)
// IsUserRegistrationRequestPermitted(u64, pid) -> bool
public long IsUserRegistrationRequestPermitted(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed.");
long Unknown = Context.RequestData.ReadInt64();
Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}");
Context.ResponseData.Write(false);
return 0;
}
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
public long TrySelectUserWithoutInteraction(ServiceCtx Context)
{
bool Unknown = Context.RequestData.ReadBoolean();
Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}");
UserProfile Profile = Context.Device.System.State.LastOpenUser;
Profile.Uuid.Write(Context.ResponseData);
return 0;
}
// InitializeApplicationInfo(u64, pid)
public long InitializeApplicationInfo(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt64();
Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}");
return 0;
}
// GetBaasAccountManagerForApplication(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication>
public long GetBaasAccountManagerForApplication(ServiceCtx Context)
{
MakeObject(Context, new IManagerForApplication());
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
MakeObject(Context, new IManagerForApplication(Uuid));
return 0;
}

View file

@ -1,36 +1,45 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Acc
{
class IManagerForApplication : IpcService
{
private UInt128 Uuid;
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IManagerForApplication()
public IManagerForApplication(UInt128 Uuid)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CheckAvailability },
{ 1, GetAccountId }
};
this.Uuid = Uuid;
}
// CheckAvailability()
public long CheckAvailability(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAcc, "Stubbed.");
return 0;
}
// GetAccountId() -> nn::account::NetworkServiceAccountId
public long GetAccountId(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed.");
long NetworkServiceAccountId = 0xcafe;
Context.ResponseData.Write(0xcafeL);
Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. NetworkServiceAccountId: {NetworkServiceAccountId}");
Context.ResponseData.Write(NetworkServiceAccountId);
return 0;
}

View file

@ -1,7 +1,7 @@
using ChocolArm64.Memory;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
using System.IO;
@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc
public long Get(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAcc, "Stubbed.");
long Position = Context.Request.ReceiveBuff[0].Position;

View file

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
long UIdLow = Context.RequestData.ReadInt64();
long UIdHigh = Context.RequestData.ReadInt64();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
Context.ResponseData.Write(0L);
@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
string Result = GetFormattedErrorCode(ErrorCode);
Context.Device.Log.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result}).");
Logger.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result}).");
return 0;
}
@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetPseudoDeviceId(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long InitializeGamePlayRecording(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
int State = Context.RequestData.ReadInt32();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
float AppletVolume = Context.RequestData.ReadSingle();
float LibraryAppletVolume = Context.RequestData.ReadSingle();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
Context.ResponseData.Write(1f);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
Context.ResponseData.Write(1f);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
float Unknown0 = Context.RequestData.ReadSingle();
long Unknown1 = Context.RequestData.ReadInt64();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
float Unknown0 = Context.RequestData.ReadSingle();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
using static Ryujinx.HLE.HOS.ErrorCode;
@ -36,7 +37,10 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
KEvent Event = Context.Process.AppletState.MessageEvent;
int Handle = Context.Process.HandleTable.OpenHandle(Event);
if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
@ -81,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
Context.ResponseData.Write((byte)0); //Unknown value.
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -103,11 +107,14 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetDefaultDisplayResolutionChangeEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(DisplayResolutionChangeEvent);
if (Context.Process.HandleTable.GenerateHandle(DisplayResolutionChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -27,18 +28,21 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long RequestToGetForeground(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long GetPopFromGeneralChannelEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(ChannelEvent);
if (Context.Process.HandleTable.GenerateHandle(ChannelEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,6 +1,7 @@
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -29,34 +30,37 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetAppletStateChangedEvent(ServiceCtx Context)
{
StateChangedEvent.Signal();
StateChangedEvent.ReadableEvent.Signal();
int Handle = Context.Process.HandleTable.OpenHandle(StateChangedEvent);
if (Context.Process.HandleTable.GenerateHandle(StateChangedEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long Start(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long GetResult(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long PushInData(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -13,6 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Am
private KEvent LaunchableEvent;
private int IdleTimeDetectionExtension;
public ISelfController(Horizon System)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -28,7 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.Am
{ 14, SetRestartMessageEnabled },
{ 16, SetOutOfFocusSuspendingEnabled },
{ 19, SetScreenShotImageOrientation },
{ 50, SetHandlesRequestToDisplay }
{ 50, SetHandlesRequestToDisplay },
{ 62, SetIdleTimeDetectionExtension },
{ 63, GetIdleTimeDetectionExtension }
};
LaunchableEvent = new KEvent(System);
@ -36,34 +41,37 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long Exit(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long LockExit(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long UnlockExit(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
public long GetLibraryAppletLaunchableEvent(ServiceCtx Context)
{
LaunchableEvent.Signal();
LaunchableEvent.ReadableEvent.Signal();
int Handle = Context.Process.HandleTable.OpenHandle(LaunchableEvent);
if (Context.Process.HandleTable.GenerateHandle(LaunchableEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -72,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -81,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -90,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -101,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -110,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -119,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -128,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
int Orientation = Context.RequestData.ReadInt32();
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
@ -137,7 +145,27 @@ namespace Ryujinx.HLE.HOS.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}
// SetIdleTimeDetectionExtension(u32)
public long SetIdleTimeDetectionExtension(ServiceCtx Context)
{
IdleTimeDetectionExtension = Context.RequestData.ReadInt32();
Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}");
return 0;
}
// GetIdleTimeDetectionExtension() -> u32
public long GetIdleTimeDetectionExtension(ServiceCtx Context)
{
Context.ResponseData.Write(IdleTimeDetectionExtension);
Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}");
return 0;
}

View file

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Am
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetAppletResourceUserId(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
Context.ResponseData.Write(0L);
@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long AcquireForegroundRights(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAm, "Stubbed.");
return 0;
}

View file

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.Logging;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Apm
@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm
Context.ResponseData.Write((uint)PerformanceConfiguration.PerformanceConfiguration1);
Context.Device.Log.PrintStub(LogClass.ServiceApm, "Stubbed.");
Logger.PrintStub(LogClass.ServiceApm, "Stubbed.");
return 0;
}

View file

@ -67,7 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioOut
public long RegisterBufferEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent);
if (Context.Process.HandleTable.GenerateHandle(ReleaseEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -1,9 +1,9 @@
using ChocolArm64.Memory;
using Ryujinx.Audio;
using Ryujinx.Audio.Adpcm;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Generic;
@ -38,6 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
private int Track;
private PlayState PlayState;
public IAudioRenderer(
Horizon System,
AMemory Memory,
@ -46,6 +48,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetSampleRate },
{ 1, GetSampleCount },
{ 2, GetMixBufferCount },
{ 3, GetState },
{ 4, RequestUpdateAudioRenderer },
{ 5, StartAudioRenderer },
{ 6, StopAudioRenderer },
@ -68,11 +74,47 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
Voices = CreateArray<VoiceContext>(Params.VoiceCount);
InitializeAudioOut();
PlayState = PlayState.Stopped;
}
// GetSampleRate() -> u32
public long GetSampleRate(ServiceCtx Context)
{
Context.ResponseData.Write(Params.SampleRate);
return 0;
}
// GetSampleCount() -> u32
public long GetSampleCount(ServiceCtx Context)
{
Context.ResponseData.Write(Params.SampleCount);
return 0;
}
// GetMixBufferCount() -> u32
public long GetMixBufferCount(ServiceCtx Context)
{
Context.ResponseData.Write(Params.MixCount);
return 0;
}
// GetState() -> u32
private long GetState(ServiceCtx Context)
{
Context.ResponseData.Write((int)PlayState);
Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. Renderer State: {Enum.GetName(typeof(PlayState), PlayState)}");
return 0;
}
private void AudioCallback()
{
UpdateEvent.Signal();
UpdateEvent.ReadableEvent.Signal();
}
private static T[] CreateArray<T>(int Size) where T : new()
@ -204,21 +246,28 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
public long StartAudioRenderer(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
PlayState = PlayState.Playing;
return 0;
}
public long StopAudioRenderer(ServiceCtx Context)
{
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
PlayState = PlayState.Stopped;
return 0;
}
public long QuerySystemEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
if (Context.Process.HandleTable.GenerateHandle(UpdateEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -1,7 +1,8 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Generic;
using System.Text;
@ -35,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
SystemEvent = new KEvent(System);
//TODO: We shouldn't be signaling this here.
SystemEvent.Signal();
SystemEvent.ReadableEvent.Signal();
}
public long ListAudioDeviceName(ServiceCtx Context)
@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
if ((Position - BasePosition) + Buffer.Length > Size)
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
break;
}
@ -79,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
string DeviceName = Encoding.ASCII.GetString(DeviceNameBuffer);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
@ -99,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
}
else
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
}
return 0;
@ -107,11 +108,14 @@ namespace Ryujinx.HLE.HOS.Services.Aud
public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
@ -120,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
{
Context.ResponseData.Write(2);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
@ -141,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
if ((Position - BasePosition) + Buffer.Length > Size)
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
break;
}
@ -164,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
string DeviceName = Encoding.UTF8.GetString(DeviceNameBuffer);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
@ -173,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
{
Context.ResponseData.Write(1f);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
@ -192,7 +196,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
}
else
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
}
return 0;
@ -200,22 +204,28 @@ namespace Ryujinx.HLE.HOS.Services.Aud
public long QueryAudioDeviceInputEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}
public long QueryAudioDeviceOutputEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
Logger.PrintStub(LogClass.ServiceAudio, "Stubbed.");
return 0;
}

View file

@ -1,9 +1,9 @@
using ChocolArm64.Memory;
using Ryujinx.Audio;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Aud.AudioOut;
using Ryujinx.HLE.Logging;
using System.Collections.Generic;
using System.Text;
@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
}
else
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!");
}
Context.ResponseData.Write(NameCount);
@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
if (DeviceName != DefaultAudioOutput)
{
Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid device name!");
Logger.PrintWarning(LogClass.Audio, "Invalid device name!");
return MakeError(ErrorModule.Audio, AudErr.DeviceNotFound);
}
@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
}
else
{
Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!");
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!");
}
int SampleRate = Context.RequestData.ReadInt32();
@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
if (SampleRate != DefaultSampleRate)
{
Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid sample rate!");
Logger.PrintWarning(LogClass.Audio, "Invalid sample rate!");
return MakeError(ErrorModule.Audio, AudErr.UnsupportedSampleRate);
}
@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
ReleaseCallback Callback = () =>
{
ReleaseEvent.Signal();
ReleaseEvent.ReadableEvent.Signal();
};
IAalOutput AudioOut = Context.Device.AudioOut;

View file

@ -1,7 +1,7 @@
using Ryujinx.Audio;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Services.Aud.AudioRenderer;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
@ -28,9 +28,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, OpenAudioRenderer },
{ 1, GetAudioRendererWorkBufferSize },
{ 2, GetAudioDevice }
{ 0, OpenAudioRenderer },
{ 1, GetAudioRendererWorkBufferSize },
{ 2, GetAudioDeviceService },
{ 4, GetAudioDeviceServiceWithRevisionInfo }
};
}
@ -102,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
Context.ResponseData.Write(Size);
Context.Device.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}.");
Logger.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}.");
return 0;
}
@ -110,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud
{
Context.ResponseData.Write(0L);
Context.Device.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!");
Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!");
return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision);
}
@ -161,13 +162,26 @@ namespace Ryujinx.HLE.HOS.Services.Aud
return Result / 8;
}
public long GetAudioDevice(ServiceCtx Context)
// GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice>
public long GetAudioDeviceService(ServiceCtx Context)
{
long UserId = Context.RequestData.ReadInt64();
long AppletResourceUserId = Context.RequestData.ReadInt64();
MakeObject(Context, new IAudioDevice(Context.Device.System));
return 0;
}
// GetAudioDeviceServiceWithRevisionInfo(nn::applet::AppletResourceUserId, u32) -> object<nn::audio::detail::IAudioDevice>
private long GetAudioDeviceServiceWithRevisionInfo(ServiceCtx Context)
{
long AppletResourceUserId = Context.RequestData.ReadInt64();
int RevisionInfo = Context.RequestData.ReadInt32();
Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " +
$"RevisionInfo: {RevisionInfo}");
return GetAudioDeviceService(Context);
}
}
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Friend
@ -15,14 +16,53 @@ namespace Ryujinx.HLE.HOS.Services.Friend
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 10101, GetFriendList },
{ 10601, DeclareCloseOnlinePlaySession },
{ 10610, UpdateUserPresence }
};
}
// nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds, array<nn::account::NetworkServiceAccountId>
public long GetFriendList(ServiceCtx Context)
{
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
int Unknown0 = Context.RequestData.ReadInt32();
FriendFilter Filter = new FriendFilter()
{
PresenceStatus = (PresenceStatusFilter)Context.RequestData.ReadInt32(),
IsFavoriteOnly = Context.RequestData.ReadBoolean(),
IsSameAppPresenceOnly = Context.RequestData.ReadBoolean(),
IsSameAppPlayedOnly = Context.RequestData.ReadBoolean(),
IsArbitraryAppPlayedOnly = Context.RequestData.ReadBoolean(),
PresenceGroupId = Context.RequestData.ReadInt64()
};
long Unknown1 = Context.RequestData.ReadInt64();
// There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
Context.ResponseData.Write(0);
Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " +
$"Unknown0: {Unknown0} - " +
$"PresenceStatus: {Filter.PresenceStatus} - " +
$"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " +
$"IsSameAppPresenceOnly: {Filter.IsSameAppPresenceOnly} - " +
$"IsSameAppPlayedOnly: {Filter.IsSameAppPlayedOnly} - " +
$"IsArbitraryAppPlayedOnly: {Filter.IsArbitraryAppPlayedOnly} - " +
$"PresenceGroupId: {Filter.PresenceGroupId} - " +
$"Unknown1: {Unknown1}");
return 0;
}
// DeclareCloseOnlinePlaySession(nn::account::Uid)
public long DeclareCloseOnlinePlaySession(ServiceCtx Context)
{
UserId Uuid = new UserId(
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
@ -31,19 +71,30 @@ namespace Ryujinx.HLE.HOS.Services.Friend
Profile.OnlinePlayState = OpenCloseState.Closed;
}
Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " +
$"OnlinePlayState: {Profile.OnlinePlayState}");
return 0;
}
// UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer<Unknown1, type: 0x19, size: 0xe0>
public long UpdateUserPresence(ServiceCtx Context)
{
UserId Uuid = new UserId(
UInt128 Uuid = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());
//TODO.
Context.Device.Log.PrintStub(LogClass.ServiceFriend, "Stubbed.");
long Unknown0 = Context.RequestData.ReadInt64();
long Position = Context.Request.PtrBuff[0].Position;
long Size = Context.Request.PtrBuff[0].Size;
//Todo: Write the buffer content.
Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " +
$"Unknown0: {Unknown0}");
return 0;
}
}
}
}

View file

@ -0,0 +1,20 @@
namespace Ryujinx.HLE.HOS.Services.Friend
{
enum PresenceStatusFilter
{
None,
Online,
OnlinePlay,
OnlineOrOnlinePlay
}
struct FriendFilter
{
public PresenceStatusFilter PresenceStatus;
public bool IsFavoriteOnly;
public bool IsSameAppPresenceOnly;
public bool IsSameAppPlayedOnly;
public bool IsArbitraryAppPlayedOnly;
public long PresenceGroupId;
}
}

View file

@ -281,11 +281,6 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (IsPathAlreadyInUse(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
IDirectory DirInterface = new IDirectory(DirName, FilterFlags);
DirInterface.Disposed += RemoveDirectoryInUse;

View file

@ -1,6 +1,6 @@
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Utilities;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.FspSrv
@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
long TitleId = Context.RequestData.ReadInt64();
UserId UserId = new UserId(
UInt128 UserId = new UInt128(
Context.RequestData.ReadInt64(),
Context.RequestData.ReadInt64());

View file

@ -0,0 +1,41 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Hid
{
public enum HidNpadJoyAssignmentMode
{
Dual,
Single,
}
public enum HidNpadHandheldActivationMode
{
Dual,
Single,
None,
}
public enum HidNpadJoyDeviceType
{
Left,
Right,
}
public enum HidNpadJoyHoldType
{
Vertical,
Horizontal,
}
[Flags]
public enum HidNpadStyle
{
None,
FullKey = 1 << 0,
Handheld = 1 << 1,
Dual = 1 << 2,
Left = 1 << 3,
Right = 1 << 4,
Invalid = 1 << 5,
}
}

View file

@ -0,0 +1,21 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
public struct HidSensorFusionParameters
{
public float RevisePower;
public float ReviseRange;
}
public struct HidAccelerometerParameters
{
public float X;
public float Y;
}
public enum HidGyroscopeZeroDriftMode
{
Loose,
Standard,
Tight
}
}

Some files were not shown because too many files have changed in this diff Show more