Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
2c5c0392f9 | |||
e0acde04bb | |||
3c61d560c3 | |||
b45a81458a | |||
460f9faf4e | |||
552c15739c | |||
0137c9e635 | |||
23fa5f4c9c | |||
4f75e26ec7 | |||
8d8983049e | |||
7969fb6bba | |||
4a4b11871e | |||
e85ee673b1 | |||
42f22fe5d7 | |||
263eb97f79 | |||
3004902257 | |||
59ddb26628 | |||
83fda10f6e | |||
d97e995e59 | |||
56b2f84702 | |||
698e36bbd2 | |||
6ce49a2dc7 | |||
ccd330ba0f | |||
95d252b7b8 | |||
add681144b | |||
c6dc00815a | |||
99f04ac1a6 | |||
ce09450743 | |||
2cb80f37d4 |
@ -13,14 +13,14 @@
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="DynamicData" Version="8.4.1" />
|
||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
@ -42,11 +42,11 @@
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -249,6 +251,10 @@ Global
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -822,6 +822,10 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
|
||||
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
||||
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
||||
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
||||
@ -1007,6 +1011,8 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
@ -1030,6 +1036,7 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
||||
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||
@ -1054,6 +1061,7 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
||||
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
||||
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
|
||||
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
|
@ -2,6 +2,8 @@ using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
@ -558,6 +560,46 @@ namespace ARMeilleure.Instructions
|
||||
EmitHsub8(context, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uqadd16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqadd8(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqsub16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqsub8(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Usat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
@ -934,6 +976,148 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
|
||||
if (saturateTo == 32)
|
||||
{
|
||||
// No saturation possible for this case.
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (saturateTo == 0)
|
||||
{
|
||||
// Result is always zero if we saturate 0 bits.
|
||||
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If the result is 0, the values are equal and we don't need saturation.
|
||||
Operand lblNoSat = Label();
|
||||
context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
|
||||
|
||||
// Saturate.
|
||||
context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
|
||||
|
||||
Operand lblExit = Label();
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
|
||||
if (saturateTo == 32)
|
||||
{
|
||||
// No saturation possible for this case.
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (saturateTo == 0)
|
||||
{
|
||||
// Result is always zero if we saturate 0 bits.
|
||||
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If the result is 0, the values are equal and we don't need saturation.
|
||||
Operand lblNoSat = Label();
|
||||
context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
|
||||
|
||||
// Saturate.
|
||||
// Assumes that the value can only underflow, since this is only used for unsigned subtraction.
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
Operand lblExit = Label();
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||
|
||||
Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
|
||||
Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
|
||||
elementAction(tempD, tempN, tempM);
|
||||
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
||||
|
||||
tempN = context.ShiftRightUI(rn, Const(16));
|
||||
tempM = context.ShiftRightUI(rm, Const(16));
|
||||
elementAction(tempD, tempN, tempM);
|
||||
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
||||
}
|
||||
|
||||
private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
|
||||
}
|
||||
|
||||
private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
|
||||
}
|
||||
|
||||
private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
|
||||
{
|
||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||
Operand result = default;
|
||||
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
|
||||
Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
nByte = context.ZeroExtend8(OperandType.I32, nByte);
|
||||
mByte = context.ZeroExtend8(OperandType.I32, mByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
nByte = context.SignExtend8(OperandType.I32, nByte);
|
||||
mByte = context.SignExtend8(OperandType.I32, mByte);
|
||||
}
|
||||
|
||||
elementAction(tempD, nByte, mByte);
|
||||
|
||||
if (b == 0)
|
||||
{
|
||||
result = context.ZeroExtend8(OperandType.I32, tempD);
|
||||
}
|
||||
else if (b < 3)
|
||||
{
|
||||
result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
@ -1246,6 +1246,33 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
||||
}
|
||||
|
||||
public static void Vqrdmulh(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
||||
{
|
||||
if (op.Size == 2)
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op2 = context.SignExtend32(OperandType.I64, op2);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(op1, op2);
|
||||
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
|
||||
res = context.ShiftRightSI(res, Const(eSize - 1));
|
||||
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
res = context.ConvertI64ToI32(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}, signed: true);
|
||||
}
|
||||
|
||||
public static void Vqsub(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
@ -191,6 +191,26 @@ namespace ARMeilleure.Instructions
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vswp(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.Q)
|
||||
{
|
||||
Operand temp = context.Copy(GetVecA32(op.Qd));
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
|
||||
context.Copy(GetVecA32(op.Qm), temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
|
||||
|
||||
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
|
||||
InsertScalar(context, op.Vm, temp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vtbl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||
|
@ -130,6 +130,36 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
||||
}
|
||||
|
||||
public static void Vsli_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = op.Shift;
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
|
||||
|
||||
Operand res = GetVec(op.Qd);
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
|
||||
|
||||
Operand neShifted = context.ShiftLeft(me, Const(shift));
|
||||
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
|
||||
|
||||
Operand deMasked = context.BitwiseAnd(de, Const(mask));
|
||||
|
||||
Operand e = context.BitwiseOr(neShifted, deMasked);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vsra(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
@ -571,6 +571,10 @@ namespace ARMeilleure.Instructions
|
||||
Umaal,
|
||||
Umlal,
|
||||
Umull,
|
||||
Uqadd16,
|
||||
Uqadd8,
|
||||
Uqsub16,
|
||||
Uqsub8,
|
||||
Usat,
|
||||
Usat16,
|
||||
Usub8,
|
||||
@ -645,6 +649,7 @@ namespace ARMeilleure.Instructions
|
||||
Vqdmulh,
|
||||
Vqmovn,
|
||||
Vqmovun,
|
||||
Vqrdmulh,
|
||||
Vqrshrn,
|
||||
Vqrshrun,
|
||||
Vqshrn,
|
||||
@ -666,6 +671,7 @@ namespace ARMeilleure.Instructions
|
||||
Vshll,
|
||||
Vshr,
|
||||
Vshrn,
|
||||
Vsli,
|
||||
Vst1,
|
||||
Vst2,
|
||||
Vst3,
|
||||
@ -682,6 +688,7 @@ namespace ARMeilleure.Instructions
|
||||
Vsub,
|
||||
Vsubl,
|
||||
Vsubw,
|
||||
Vswp,
|
||||
Vtbl,
|
||||
Vtrn,
|
||||
Vtst,
|
||||
|
@ -80,7 +80,10 @@ namespace ARMeilleure.Translation
|
||||
return true;
|
||||
}
|
||||
|
||||
Monitor.Wait(Sync);
|
||||
if (!_disposed)
|
||||
{
|
||||
Monitor.Wait(Sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,16 +18,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
|
||||
if (version == 2)
|
||||
{
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
|
||||
PerformanceEntryVersion2,
|
||||
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
}
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1,
|
||||
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");
|
||||
|
@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
{
|
||||
performanceEntry = null;
|
||||
|
||||
if (_entryDetailIndex > MaxFrameDetailCount)
|
||||
if (_entryDetailIndex >= MaxFrameDetailCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
||||
};
|
||||
|
||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
|
||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
|
||||
|
||||
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
public partial class ByteMemoryPool
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
|
||||
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
|
||||
/// with a length of the requested size.
|
||||
/// </summary>
|
||||
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
|
||||
{
|
||||
private byte[] _array;
|
||||
private readonly int _length;
|
||||
|
||||
public ByteMemoryPoolBuffer(int length)
|
||||
{
|
||||
_array = ArrayPool<byte>.Shared.Rent(length);
|
||||
_length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
|
||||
/// </summary>
|
||||
public Memory<byte> Memory
|
||||
{
|
||||
get
|
||||
{
|
||||
byte[] array = _array;
|
||||
|
||||
ObjectDisposedException.ThrowIf(array is null, this);
|
||||
|
||||
return new Memory<byte>(array, 0, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var array = Interlocked.Exchange(ref _array, null);
|
||||
|
||||
if (array != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a pool of re-usable byte array instances.
|
||||
/// </summary>
|
||||
public static partial class ByteMemoryPool
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the maximum buffer size supported by this pool.
|
||||
/// </summary>
|
||||
public static int MaxBufferSize => Array.MaxLength;
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer may contain data from a prior use.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> Rent(long length)
|
||||
=> RentImpl(checked((int)length));
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer may contain data from a prior use.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> Rent(ulong length)
|
||||
=> RentImpl(checked((int)length));
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer may contain data from a prior use.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> Rent(int length)
|
||||
=> RentImpl(length);
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> RentCleared(long length)
|
||||
=> RentCleared(checked((int)length));
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> RentCleared(ulong length)
|
||||
=> RentCleared(checked((int)length));
|
||||
|
||||
/// <summary>
|
||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||
/// </summary>
|
||||
/// <param name="length">The buffer's required length in bytes</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IMemoryOwner<byte> RentCleared(int length)
|
||||
{
|
||||
var buffer = RentImpl(length);
|
||||
|
||||
buffer.Memory.Span.Clear();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The byte buffer to copy</param>
|
||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
|
||||
public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var copy = RentImpl(buffer.Length);
|
||||
|
||||
buffer.CopyTo(copy.Memory.Span);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static ByteMemoryPoolBuffer RentImpl(int length)
|
||||
{
|
||||
if ((uint)length > Array.MaxLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length), length, null);
|
||||
}
|
||||
|
||||
return new ByteMemoryPoolBuffer(length);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -42,14 +42,14 @@ namespace Ryujinx.Common
|
||||
return StreamUtils.StreamToBytes(stream);
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
||||
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
||||
{
|
||||
var (assembly, path) = ResolveManifestPath(filename);
|
||||
|
||||
return ReadFileToRentedMemory(assembly, path);
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
||||
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
||||
{
|
||||
using var stream = GetStream(assembly, filename);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
using Microsoft.IO;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -16,7 +15,7 @@ namespace Ryujinx.Common.Utilities
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
|
||||
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
|
||||
{
|
||||
if (input is MemoryStream inputMemoryStream)
|
||||
{
|
||||
@ -26,9 +25,9 @@ namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
long bytesExpected = input.Length;
|
||||
|
||||
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
|
||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
||||
|
||||
var destSpan = ownedMemory.Memory.Span;
|
||||
var destSpan = ownedMemory.Span;
|
||||
|
||||
int totalBytesRead = 0;
|
||||
|
||||
@ -66,14 +65,14 @@ namespace Ryujinx.Common.Utilities
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
||||
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
||||
{
|
||||
input.Position = 0;
|
||||
|
||||
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
|
||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
|
||||
|
||||
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
||||
_ = input.Read(ownedMemory.Memory.Span);
|
||||
_ = input.Read(ownedMemory.Span);
|
||||
|
||||
return ownedMemory;
|
||||
}
|
||||
|
@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
else
|
||||
{
|
||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
Read(va, memoryOwner.Memory.Span);
|
||||
Read(va, memoryOwner.Span);
|
||||
|
||||
return new WritableRegion(this, va, memoryOwner);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
|
@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
{
|
||||
context.Arm64Assembler.Add(d, n, m);
|
||||
EmitSaturateUnsignedRange(context, d, 16);
|
||||
EmitSaturateUqadd(context, d, 16);
|
||||
});
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
{
|
||||
context.Arm64Assembler.Add(d, n, m);
|
||||
EmitSaturateUnsignedRange(context, d, 8);
|
||||
EmitSaturateUqadd(context, d, 8);
|
||||
});
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
context.Arm64Assembler.Add(d, n, m);
|
||||
}
|
||||
|
||||
EmitSaturateUnsignedRange(context, d, 16);
|
||||
EmitSaturateUq(context, d, 16, e == 0);
|
||||
});
|
||||
}
|
||||
|
||||
@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
context.Arm64Assembler.Sub(d, n, m);
|
||||
}
|
||||
|
||||
EmitSaturateUnsignedRange(context, d, 16);
|
||||
EmitSaturateUq(context, d, 16, e != 0);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||
{
|
||||
InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
{
|
||||
context.Arm64Assembler.Sub(d, n, m);
|
||||
EmitSaturateUnsignedRange(context, d, 16);
|
||||
EmitSaturateUqsub(context, d, 16);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||
{
|
||||
InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||
{
|
||||
context.Arm64Assembler.Sub(d, n, m);
|
||||
EmitSaturateUnsignedRange(context, d, 8);
|
||||
EmitSaturateUqsub(context, d, 8);
|
||||
});
|
||||
}
|
||||
|
||||
@ -358,7 +358,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
|
||||
private static void EmitSaturateUqadd(CodeGenContext context, Operand value, uint saturateTo)
|
||||
{
|
||||
EmitSaturateUq(context, value, saturateTo, isSub: false);
|
||||
}
|
||||
|
||||
private static void EmitSaturateUqsub(CodeGenContext context, Operand value, uint saturateTo)
|
||||
{
|
||||
EmitSaturateUq(context, value, saturateTo, isSub: true);
|
||||
}
|
||||
|
||||
private static void EmitSaturateUq(CodeGenContext context, Operand value, uint saturateTo, bool isSub)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
|
||||
@ -379,7 +389,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
return;
|
||||
}
|
||||
|
||||
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo));
|
||||
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo));
|
||||
|
||||
int branchIndex = context.CodeWriter.InstructionPointer;
|
||||
|
||||
@ -387,7 +397,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
|
||||
|
||||
// Saturate.
|
||||
context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo));
|
||||
context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo));
|
||||
|
||||
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
||||
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -145,9 +144,9 @@ namespace Ryujinx.Graphics.Device
|
||||
}
|
||||
else
|
||||
{
|
||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
|
||||
ReadImpl(va, memoryOwner.Span);
|
||||
|
||||
return new WritableRegion(this, va, memoryOwner, tracked: true);
|
||||
}
|
||||
|
@ -39,7 +39,10 @@ namespace Ryujinx.Graphics.Device
|
||||
{
|
||||
var field = fields[fieldIndex];
|
||||
|
||||
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
||||
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
|
||||
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
|
||||
|
||||
int sizeOfField = nextFieldOffset - currentFieldOffset;
|
||||
|
||||
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
||||
{
|
||||
|
@ -1,63 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ryujinx.Graphics.Device
|
||||
{
|
||||
public static class SizeCalculator
|
||||
{
|
||||
public static int SizeOf(Type type)
|
||||
{
|
||||
// Is type a enum type?
|
||||
if (type.IsEnum)
|
||||
{
|
||||
type = type.GetEnumUnderlyingType();
|
||||
}
|
||||
|
||||
// Is type a pointer type?
|
||||
if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr))
|
||||
{
|
||||
return IntPtr.Size;
|
||||
}
|
||||
|
||||
// Is type a struct type?
|
||||
if (type.IsValueType && !type.IsPrimitive)
|
||||
{
|
||||
// Check if the struct has a explicit size, if so, return that.
|
||||
if (type.StructLayoutAttribute.Size != 0)
|
||||
{
|
||||
return type.StructLayoutAttribute.Size;
|
||||
}
|
||||
|
||||
// Otherwise we calculate the sum of the sizes of all fields.
|
||||
int size = 0;
|
||||
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
|
||||
{
|
||||
size += SizeOf(fields[fieldIndex].FieldType);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// Primitive types.
|
||||
return (Type.GetTypeCode(type)) switch
|
||||
{
|
||||
TypeCode.SByte => sizeof(sbyte),
|
||||
TypeCode.Byte => sizeof(byte),
|
||||
TypeCode.Int16 => sizeof(short),
|
||||
TypeCode.UInt16 => sizeof(ushort),
|
||||
TypeCode.Int32 => sizeof(int),
|
||||
TypeCode.UInt32 => sizeof(uint),
|
||||
TypeCode.Int64 => sizeof(long),
|
||||
TypeCode.UInt64 => sizeof(ulong),
|
||||
TypeCode.Char => sizeof(char),
|
||||
TypeCode.Single => sizeof(float),
|
||||
TypeCode.Double => sizeof(double),
|
||||
TypeCode.Decimal => sizeof(decimal),
|
||||
TypeCode.Boolean => sizeof(bool),
|
||||
_ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
@ -113,25 +112,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int GetLevelsClamped()
|
||||
{
|
||||
int maxSize = Width;
|
||||
|
||||
if (Target != Target.Texture1D &&
|
||||
Target != Target.Texture1DArray)
|
||||
{
|
||||
maxSize = Math.Max(maxSize, Height);
|
||||
}
|
||||
|
||||
if (Target == Target.Texture3D)
|
||||
{
|
||||
maxSize = Math.Max(maxSize, Depth);
|
||||
}
|
||||
|
||||
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
|
||||
return Math.Min(Levels, maxLevels);
|
||||
}
|
||||
|
||||
private static int GetLevelSize(int size, int level)
|
||||
{
|
||||
return Math.Max(1, size >> level);
|
||||
|
@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
||||
if (target != null)
|
||||
{
|
||||
target.SynchronizeMemory();
|
||||
var dataCopy = ByteMemoryPool.RentCopy(data);
|
||||
var dataCopy = MemoryOwner<byte>.RentCopy(data);
|
||||
target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
|
||||
target.SignalModified();
|
||||
|
||||
|
@ -79,7 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
var field = fields[fieldIndex];
|
||||
|
||||
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
||||
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
|
||||
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
|
||||
|
||||
int sizeOfField = nextFieldOffset - currentFieldOffset;
|
||||
|
||||
if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
@ -805,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
sliceDepth,
|
||||
levels,
|
||||
layers,
|
||||
out IMemoryOwner<byte> decoded))
|
||||
out MemoryOwner<byte> decoded))
|
||||
{
|
||||
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
|
||||
|
||||
|
@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
|
||||
public bool SamplerPoolModified()
|
||||
{
|
||||
return SamplerPool.WasModified(ref _samplerPoolSequence);
|
||||
return SamplerPool != null && SamplerPool.WasModified(ref _samplerPoolSequence);
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,12 +516,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
|
||||
// Check if any of our cached samplers changed on the pool.
|
||||
foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
|
||||
if (SamplerPool != null)
|
||||
{
|
||||
if (SamplerPool.GetCachedItem(samplerId) != sampler ||
|
||||
(sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
|
||||
foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
|
||||
{
|
||||
return true;
|
||||
if (SamplerPool.GetCachedItem(samplerId) != sampler ||
|
||||
(sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -899,13 +902,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
Sampler sampler = samplerPool?.Get(samplerId);
|
||||
|
||||
entry.TextureIds[textureId] = (texture, descriptor);
|
||||
entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
|
||||
|
||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||
ISampler hostSampler = null;
|
||||
|
||||
if (!isImage && bindingInfo.Target != Target.TextureBuffer)
|
||||
{
|
||||
Sampler sampler = samplerPool?.Get(samplerId);
|
||||
|
||||
entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
|
||||
|
||||
hostSampler = sampler?.GetHostSampler(texture);
|
||||
}
|
||||
|
||||
Format format = bindingInfo.Format;
|
||||
|
||||
|
@ -6,6 +6,7 @@ using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
@ -490,6 +491,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
levels = (maxLod - minLod) + 1;
|
||||
}
|
||||
|
||||
levels = ClampLevels(target, width, height, depthOrLayers, levels);
|
||||
|
||||
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
|
||||
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
|
||||
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
|
||||
@ -540,6 +543,34 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
swizzleA);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions.
|
||||
/// </summary>
|
||||
/// <param name="target">Number of texture dimensions (1D, 2D, 3D, Cube, etc)</param>
|
||||
/// <param name="width">Width of the texture</param>
|
||||
/// <param name="height">Height of the texture, ignored for 1D textures</param>
|
||||
/// <param name="depthOrLayers">Depth of the texture for 3D textures, otherwise ignored</param>
|
||||
/// <param name="levels">Original amount of mipmap levels</param>
|
||||
/// <returns>Clamped mipmap levels</returns>
|
||||
private static int ClampLevels(Target target, int width, int height, int depthOrLayers, int levels)
|
||||
{
|
||||
int maxSize = width;
|
||||
|
||||
if (target != Target.Texture1D &&
|
||||
target != Target.Texture1DArray)
|
||||
{
|
||||
maxSize = Math.Max(maxSize, height);
|
||||
}
|
||||
|
||||
if (target == Target.Texture3D)
|
||||
{
|
||||
maxSize = Math.Max(maxSize, depthOrLayers);
|
||||
}
|
||||
|
||||
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
|
||||
return Math.Min(levels, maxLevels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
|
||||
/// The depth-stencil mode is determined based on how the driver sets those parameters.
|
||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -242,9 +241,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
|
||||
ReadImpl(va, memoryOwner.Span, tracked);
|
||||
|
||||
return new WritableRegion(this, va, memoryOwner, tracked);
|
||||
}
|
||||
|
@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize());
|
||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(checked((int)range.GetSize()));
|
||||
|
||||
Memory<byte> memory = memoryOwner.Memory;
|
||||
Span<byte> memorySpan = memoryOwner.Span;
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
int size = (int)currentRange.Size;
|
||||
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size));
|
||||
GetSpan(currentRange.Address, size).CopyTo(memorySpan.Slice(offset, size));
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 6921;
|
||||
private const uint CodeGenVersion = 7131;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -1,6 +1,5 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
@ -10,11 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
static class FormatConverter
|
||||
{
|
||||
public unsafe static IMemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
|
||||
public unsafe static MemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
|
||||
{
|
||||
IMemoryOwner<byte> outputMemory = ByteMemoryPool.Rent(data.Length);
|
||||
MemoryOwner<byte> outputMemory = MemoryOwner<byte>.Rent(data.Length);
|
||||
|
||||
Span<byte> output = outputMemory.Memory.Span;
|
||||
Span<byte> output = outputMemory.Span;
|
||||
|
||||
int start = 0;
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
|
||||
}
|
||||
|
||||
int levels = Info.GetLevelsClamped();
|
||||
int levels = Info.Levels;
|
||||
|
||||
switch (Info.Target)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
pixelInternalFormat = format.PixelInternalFormat;
|
||||
}
|
||||
|
||||
int levels = Info.GetLevelsClamped();
|
||||
int levels = Info.Levels;
|
||||
|
||||
GL.TextureView(
|
||||
Handle,
|
||||
@ -267,7 +267,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
public unsafe PinnedSpan<byte> GetData()
|
||||
{
|
||||
int size = 0;
|
||||
int levels = Info.GetLevelsClamped();
|
||||
int levels = Info.Levels;
|
||||
|
||||
for (int level = 0; level < levels; level++)
|
||||
{
|
||||
@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
faces = 6;
|
||||
}
|
||||
|
||||
int levels = Info.GetLevelsClamped();
|
||||
int levels = Info.Levels;
|
||||
|
||||
for (int level = 0; level < levels; level++)
|
||||
{
|
||||
@ -716,7 +716,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
int depth = Info.Depth;
|
||||
int levels = Info.GetLevelsClamped();
|
||||
int levels = Info.Levels;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
|
@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
||||
}
|
||||
|
||||
if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
|
||||
for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++)
|
||||
{
|
||||
localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister());
|
||||
Operand dest = operation.GetDest(dstIndex);
|
||||
|
||||
if (dest != null && dest.Type == OperandType.Register)
|
||||
{
|
||||
localOutputs[block.Index] |= GetMask(dest.GetRegister());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -293,9 +292,9 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||
int depth,
|
||||
int levels,
|
||||
int layers,
|
||||
out IMemoryOwner<byte> decoded)
|
||||
out MemoryOwner<byte> decoded)
|
||||
{
|
||||
decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
|
||||
decoded = MemoryOwner<byte>.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
|
||||
|
||||
AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
private const int BlockWidth = 4;
|
||||
private const int BlockHeight = 4;
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -23,12 +22,12 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||
|
||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||
|
||||
@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -111,12 +110,12 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||
|
||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||
|
||||
@ -197,7 +196,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -206,13 +205,13 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||
Span<byte> rPal = stackalloc byte[8];
|
||||
|
||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||
|
||||
@ -294,7 +293,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
public static MemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -306,8 +305,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
||||
int alignedWidth = BitUtils.AlignUp(width, 4);
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
Span<byte> outputSpan = output.Memory.Span;
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
Span<byte> outputSpan = output.Span;
|
||||
|
||||
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
||||
|
||||
@ -402,7 +401,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
public static MemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -414,7 +413,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
||||
int alignedWidth = BitUtils.AlignUp(width, 2);
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
||||
|
||||
@ -423,7 +422,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
Span<byte> rPal = stackalloc byte[8];
|
||||
Span<byte> gPal = stackalloc byte[8];
|
||||
|
||||
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
|
||||
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Span);
|
||||
|
||||
Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
|
||||
Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
|
||||
@ -527,7 +526,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
public static MemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -536,8 +535,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
Span<byte> outputSpan = output.Memory.Span;
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
Span<byte> outputSpan = output.Span;
|
||||
|
||||
int inputOffset = 0;
|
||||
int outputOffset = 0;
|
||||
@ -566,7 +565,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -575,8 +574,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
Span<byte> outputSpan = output.Memory.Span;
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
Span<byte> outputSpan = output.Span;
|
||||
|
||||
int inputOffset = 0;
|
||||
int outputOffset = 0;
|
||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Texture.Encoders;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
private const int BlockWidth = 4;
|
||||
private const int BlockHeight = 4;
|
||||
|
||||
public static IMemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
size += w * h * 16 * Math.Max(1, depth >> l) * layers;
|
||||
}
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||
Memory<byte> outputMemory = output.Memory;
|
||||
|
||||
int imageBaseIOffs = 0;
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -51,15 +50,15 @@ namespace Ryujinx.Graphics.Texture
|
||||
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
|
||||
};
|
||||
|
||||
public static IMemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||
|
||||
int inputOffset = 0;
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||
|
||||
int imageBaseOOffs = 0;
|
||||
@ -113,15 +112,15 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||
|
||||
int inputOffset = 0;
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||
|
||||
int imageBaseOOffs = 0;
|
||||
@ -170,15 +169,15 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
public static MemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||
{
|
||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||
|
||||
int inputOffset = 0;
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||
|
||||
int imageBaseOOffs = 0;
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.Intrinsics;
|
||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||
|
||||
@ -95,7 +94,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
};
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertBlockLinearToLinear(
|
||||
public static MemoryOwner<byte> ConvertBlockLinearToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
@ -121,8 +120,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
blockHeight,
|
||||
bytesPerPixel);
|
||||
|
||||
IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize);
|
||||
Span<byte> output = outputOwner.Memory.Span;
|
||||
MemoryOwner<byte> outputOwner = MemoryOwner<byte>.Rent(outSize);
|
||||
Span<byte> output = outputOwner.Span;
|
||||
|
||||
int outOffs = 0;
|
||||
|
||||
@ -249,7 +248,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return outputOwner;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertLinearStridedToLinear(
|
||||
public static MemoryOwner<byte> ConvertLinearStridedToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int blockWidth,
|
||||
@ -265,8 +264,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
||||
lineSize = Math.Min(lineSize, outStride);
|
||||
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(h * outStride);
|
||||
Span<byte> outSpan = output.Memory.Span;
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(h * outStride);
|
||||
Span<byte> outSpan = output.Span;
|
||||
|
||||
int outOffs = 0;
|
||||
int inOffs = 0;
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
@ -21,13 +20,14 @@ namespace Ryujinx.Graphics.Texture
|
||||
return (remainder, outRemainder, length / stride);
|
||||
}
|
||||
|
||||
public unsafe static IMemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
|
||||
public unsafe static MemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
|
||||
{
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||
Span<byte> outputSpan = output.Span;
|
||||
|
||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
|
||||
|
||||
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
|
||||
Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
|
||||
|
||||
if (remainder == 0)
|
||||
{
|
||||
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
int sizeTrunc = data.Length & ~7;
|
||||
start = sizeTrunc;
|
||||
|
||||
fixed (byte* inputPtr = data, outputPtr = output.Memory.Span)
|
||||
fixed (byte* inputPtr = data, outputPtr = outputSpan)
|
||||
{
|
||||
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
|
||||
{
|
||||
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
|
||||
for (int i = start; i < data.Length; i++)
|
||||
{
|
||||
outputSpan[i] = data[i];
|
||||
outputSpanUInt16[i] = data[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
outputSpan[outOffset++] = data[offset++];
|
||||
outputSpanUInt16[outOffset++] = data[offset++];
|
||||
}
|
||||
|
||||
offset += remainder;
|
||||
@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
public static MemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
{
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||
int offset = 0;
|
||||
int outOffset = 0;
|
||||
|
||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||
|
||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
|
||||
public static MemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
|
||||
{
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||
int offset = 0;
|
||||
int outOffset = 0;
|
||||
|
||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||
|
||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
public static MemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
{
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||
int offset = 0;
|
||||
int outOffset = 0;
|
||||
|
||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||
|
||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IMemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
public static MemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||
{
|
||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
||||
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||
int offset = 0;
|
||||
int outOffset = 0;
|
||||
|
||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||
|
||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
|
@ -211,7 +211,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4);
|
||||
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
||||
_dummyTexture.SetData(dummyTextureData);
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_storages = null;
|
||||
SetDirty(_gd);
|
||||
SetDirty(_gd, isImage: true);
|
||||
}
|
||||
|
||||
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
||||
|
@ -439,7 +439,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
|
||||
PrimitiveRestartEnable = primitiveRestartEnable,
|
||||
Topology = Topology,
|
||||
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
|
||||
};
|
||||
|
||||
var tessellationState = new PipelineTessellationStateCreateInfo
|
||||
|
@ -14,13 +14,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private int _bindCount;
|
||||
|
||||
protected void SetDirty(VulkanRenderer gd)
|
||||
protected void SetDirty(VulkanRenderer gd, bool isImage)
|
||||
{
|
||||
ReleaseDescriptorSet();
|
||||
|
||||
if (_bindCount != 0)
|
||||
{
|
||||
gd.PipelineInternal.ForceTextureDirty();
|
||||
if (isImage)
|
||||
{
|
||||
gd.PipelineInternal.ForceImageDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
gd.PipelineInternal.ForceTextureDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_storages = null;
|
||||
SetDirty(_gd);
|
||||
SetDirty(_gd, isImage: false);
|
||||
}
|
||||
|
||||
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
||||
|
@ -667,8 +667,36 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
|
||||
{
|
||||
// No barrier necessary, as this is a temporary copy buffer.
|
||||
offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
_gd,
|
||||
cbs.CommandBuffer,
|
||||
copyToBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.TransferWriteBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.TransferBit,
|
||||
offset,
|
||||
outSize);
|
||||
}
|
||||
|
||||
InsertImageBarrier(
|
||||
_gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
image,
|
||||
TextureStorage.DefaultAccessMask,
|
||||
AccessFlags.TransferReadBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.TransferBit,
|
||||
Info.Format.ConvertAspectFlags(),
|
||||
FirstLayer + layer,
|
||||
FirstLevel + level,
|
||||
1,
|
||||
1);
|
||||
|
||||
CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
|
||||
|
||||
@ -677,6 +705,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
|
||||
tempCopyHolder.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
_gd,
|
||||
cbs.CommandBuffer,
|
||||
copyToBuffer,
|
||||
AccessFlags.TransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.TransferBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
offset,
|
||||
outSize);
|
||||
}
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
|
||||
|
@ -13,7 +13,6 @@ using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Common.SystemInfo;
|
||||
using Ryujinx.UI.Widgets;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -162,12 +161,6 @@ namespace Ryujinx
|
||||
});
|
||||
};
|
||||
|
||||
// Sets ImageSharp Jpeg Encoder Quality.
|
||||
SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
|
||||
{
|
||||
Quality = 100,
|
||||
});
|
||||
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
|
||||
@ -256,6 +249,12 @@ namespace Ryujinx
|
||||
MainWindow mainWindow = new();
|
||||
mainWindow.Show();
|
||||
|
||||
// Load the game table if no application was requested by the command line
|
||||
if (CommandLineState.LaunchPathArg == null)
|
||||
{
|
||||
mainWindow.UpdateGameTable();
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount;
|
||||
|
@ -30,7 +30,6 @@
|
||||
<PackageReference Include="OpenTK.Graphics" />
|
||||
<PackageReference Include="SPB" />
|
||||
<PackageReference Include="SharpZipLib" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -187,7 +187,10 @@ namespace Ryujinx.UI
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
// Instantiate GUI objects.
|
||||
ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel);
|
||||
ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel)
|
||||
{
|
||||
DesiredLanguage = ConfigurationState.Instance.System.Language,
|
||||
};
|
||||
_uiHandler = new GtkHostUIHandler(this);
|
||||
_deviceExitStatus = new AutoResetEvent(false);
|
||||
|
||||
@ -325,7 +328,6 @@ namespace Ryujinx.UI
|
||||
_hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString());
|
||||
|
||||
UpdateColumns();
|
||||
UpdateGameTable();
|
||||
|
||||
ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) =>
|
||||
{
|
||||
@ -738,7 +740,8 @@ namespace Ryujinx.UI
|
||||
|
||||
Thread applicationLibraryThread = new(() =>
|
||||
{
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language);
|
||||
ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs);
|
||||
|
||||
_updatingGameTable = false;
|
||||
})
|
||||
|
@ -13,16 +13,13 @@ using Ryujinx.Input.HLE;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Widgets;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
@ -404,23 +401,31 @@ namespace Ryujinx.UI
|
||||
return;
|
||||
}
|
||||
|
||||
Image image = e.IsBgra ? Image.LoadPixelData<Bgra32>(e.Data, e.Width, e.Height)
|
||||
: Image.LoadPixelData<Rgba32>(e.Data, e.Width, e.Height);
|
||||
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
|
||||
using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
|
||||
|
||||
if (e.FlipX)
|
||||
Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length);
|
||||
using var surface = SKSurface.Create(image.Info);
|
||||
var canvas = surface.Canvas;
|
||||
|
||||
if (e.FlipX || e.FlipY)
|
||||
{
|
||||
image.Mutate(x => x.Flip(FlipMode.Horizontal));
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
|
||||
float scaleX = e.FlipX ? -1 : 1;
|
||||
float scaleY = e.FlipY ? -1 : 1;
|
||||
|
||||
var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f);
|
||||
|
||||
canvas.SetMatrix(matrix);
|
||||
}
|
||||
canvas.DrawBitmap(image, new SKPoint());
|
||||
|
||||
if (e.FlipY)
|
||||
{
|
||||
image.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
}
|
||||
|
||||
image.SaveAsPng(path, new PngEncoder()
|
||||
{
|
||||
ColorType = PngColorType.Rgb,
|
||||
});
|
||||
surface.Flush();
|
||||
using var snapshot = surface.Snapshot();
|
||||
using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80);
|
||||
using var file = File.OpenWrite(path);
|
||||
encoded.SaveTo(file);
|
||||
|
||||
image.Dispose();
|
||||
|
||||
|
@ -473,7 +473,7 @@ namespace Ryujinx.UI.Widgets
|
||||
|
||||
private void ManageDlc_Clicked(object sender, EventArgs args)
|
||||
{
|
||||
new DlcWindow(_virtualFileSystem, _applicationData.IdString, _applicationData).Show();
|
||||
new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show();
|
||||
}
|
||||
|
||||
private void ManageCheats_Clicked(object sender, EventArgs args)
|
||||
|
@ -9,16 +9,13 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.UI.Windows
|
||||
{
|
||||
@ -144,9 +141,11 @@ namespace Ryujinx.UI.Windows
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
|
||||
using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888));
|
||||
var data = DecompressYaz0(stream);
|
||||
Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length);
|
||||
|
||||
avatarImage.SaveAsPng(streamPng);
|
||||
avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80);
|
||||
|
||||
_avatarDict.Add(item.FullPath, streamPng.ToArray());
|
||||
}
|
||||
@ -170,15 +169,23 @@ namespace Ryujinx.UI.Windows
|
||||
{
|
||||
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
|
||||
|
||||
Image avatarImage = Image.Load(data, new PngDecoder());
|
||||
using var avatarImage = SKBitmap.Decode(data);
|
||||
using var surface = SKSurface.Create(avatarImage.Info);
|
||||
|
||||
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
|
||||
var background = new SKColor(
|
||||
(byte)(_backgroundColor.Red * 255),
|
||||
(byte)(_backgroundColor.Green * 255),
|
||||
(byte)(_backgroundColor.Blue * 255),
|
||||
(byte)(_backgroundColor.Alpha * 255)
|
||||
)));
|
||||
avatarImage.SaveAsJpeg(streamJpg);
|
||||
);
|
||||
var canvas = surface.Canvas;
|
||||
canvas.Clear(background);
|
||||
canvas.DrawBitmap(avatarImage, new SKPoint());
|
||||
|
||||
surface.Flush();
|
||||
using var snapshot = surface.Snapshot();
|
||||
using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||
encoded.SaveTo(streamJpg);
|
||||
|
||||
return streamJpg.ToArray();
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.UI.Windows
|
||||
public class DlcWindow : Window
|
||||
{
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly string _applicationId;
|
||||
private readonly string _applicationIdBase;
|
||||
private readonly string _dlcJsonPath;
|
||||
private readonly List<DownloadableContentContainer> _dlcContainerList;
|
||||
|
||||
@ -36,16 +36,16 @@ namespace Ryujinx.UI.Windows
|
||||
[GUI] TreeSelection _dlcTreeSelection;
|
||||
#pragma warning restore CS0649, IDE0044
|
||||
|
||||
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, titleId, applicationData) { }
|
||||
public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { }
|
||||
|
||||
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
|
||||
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
|
||||
{
|
||||
builder.Autoconnect(this);
|
||||
|
||||
_applicationId = applicationId;
|
||||
_applicationIdBase = applicationIdBase;
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json");
|
||||
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationId.ToUpper()}]";
|
||||
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json");
|
||||
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]";
|
||||
|
||||
try
|
||||
{
|
||||
@ -163,7 +163,7 @@ namespace Ryujinx.UI.Windows
|
||||
|
||||
if (nca.Header.ContentType == NcaContentType.PublicData)
|
||||
{
|
||||
if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL))
|
||||
if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ namespace Ryujinx.UI.Windows
|
||||
|
||||
_applicationData = applicationData;
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json");
|
||||
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json");
|
||||
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
|
||||
|
||||
try
|
||||
@ -67,7 +67,7 @@ namespace Ryujinx.UI.Windows
|
||||
};
|
||||
}
|
||||
|
||||
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]";
|
||||
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]";
|
||||
|
||||
// Try to get updates from PFS first
|
||||
AddUpdate(_applicationData.Path, true);
|
||||
|
@ -4,15 +4,13 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Widgets;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
|
||||
namespace Ryujinx.UI.Windows
|
||||
{
|
||||
@ -177,13 +175,13 @@ namespace Ryujinx.UI.Windows
|
||||
|
||||
private void ProcessProfileImage(byte[] buffer)
|
||||
{
|
||||
using Image image = Image.Load(buffer);
|
||||
using var image = SKBitmap.Decode(buffer);
|
||||
|
||||
image.Mutate(x => x.Resize(256, 256));
|
||||
image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High);
|
||||
|
||||
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
|
||||
|
||||
image.SaveAsJpeg(streamJpg);
|
||||
image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80);
|
||||
|
||||
_bufferImageProfile = streamJpg.ToArray();
|
||||
}
|
||||
|
63
src/Ryujinx.HLE.Generators/CodeGenerator.cs
Normal file
63
src/Ryujinx.HLE.Generators/CodeGenerator.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.Generators
|
||||
{
|
||||
class CodeGenerator
|
||||
{
|
||||
private const int IndentLength = 4;
|
||||
|
||||
private readonly StringBuilder _sb;
|
||||
private int _currentIndentCount;
|
||||
|
||||
public CodeGenerator()
|
||||
{
|
||||
_sb = new StringBuilder();
|
||||
}
|
||||
|
||||
public void EnterScope(string header = null)
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
AppendLine(header);
|
||||
}
|
||||
|
||||
AppendLine("{");
|
||||
IncreaseIndentation();
|
||||
}
|
||||
|
||||
public void LeaveScope(string suffix = "")
|
||||
{
|
||||
DecreaseIndentation();
|
||||
AppendLine($"}}{suffix}");
|
||||
}
|
||||
|
||||
public void IncreaseIndentation()
|
||||
{
|
||||
_currentIndentCount++;
|
||||
}
|
||||
|
||||
public void DecreaseIndentation()
|
||||
{
|
||||
if (_currentIndentCount - 1 >= 0)
|
||||
{
|
||||
_currentIndentCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public void AppendLine()
|
||||
{
|
||||
_sb.AppendLine();
|
||||
}
|
||||
|
||||
public void AppendLine(string text)
|
||||
{
|
||||
_sb.Append(' ', IndentLength * _currentIndentCount);
|
||||
_sb.AppendLine(text);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
76
src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
Normal file
76
src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.Generators
|
||||
{
|
||||
[Generator]
|
||||
public class IpcServiceGenerator : ISourceGenerator
|
||||
{
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
|
||||
CodeGenerator generator = new CodeGenerator();
|
||||
|
||||
generator.AppendLine("using System;");
|
||||
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
|
||||
generator.EnterScope($"partial class IUserInterface");
|
||||
|
||||
generator.EnterScope($"public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)");
|
||||
foreach (var className in syntaxReceiver.Types)
|
||||
{
|
||||
if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
|
||||
continue;
|
||||
var name = GetFullName(className, context).Replace("global::", "");
|
||||
if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
|
||||
continue;
|
||||
var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax);
|
||||
|
||||
if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1))
|
||||
continue;
|
||||
|
||||
if (constructors.Where(x => x.ParameterList.Parameters.Count >= 1).FirstOrDefault().ParameterList.Parameters[0].Type.ToString() == "ServiceCtx")
|
||||
{
|
||||
generator.EnterScope($"if (type == typeof({GetFullName(className, context)}))");
|
||||
if (constructors.Any(x => x.ParameterList.Parameters.Count == 2))
|
||||
{
|
||||
var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type;
|
||||
var model = context.Compilation.GetSemanticModel(type.SyntaxTree);
|
||||
var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol;
|
||||
var fullName = typeSymbol.ToString();
|
||||
generator.EnterScope("if (parameter != null)");
|
||||
generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);");
|
||||
generator.LeaveScope();
|
||||
}
|
||||
|
||||
if (constructors.Any(x => x.ParameterList.Parameters.Count == 1))
|
||||
{
|
||||
generator.AppendLine($"return new {GetFullName(className, context)}(context);");
|
||||
}
|
||||
|
||||
generator.LeaveScope();
|
||||
}
|
||||
}
|
||||
|
||||
generator.AppendLine("return null;");
|
||||
generator.LeaveScope();
|
||||
|
||||
generator.LeaveScope();
|
||||
generator.LeaveScope();
|
||||
context.AddSource($"IUserInterface.g.cs", generator.ToString());
|
||||
}
|
||||
|
||||
private string GetFullName(ClassDeclarationSyntax syntaxNode, GeneratorExecutionContext context)
|
||||
{
|
||||
var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode);
|
||||
|
||||
return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
}
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new ServiceSyntaxReceiver());
|
||||
}
|
||||
}
|
||||
}
|
19
src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
Normal file
19
src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
Normal file
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
24
src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
Normal file
24
src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Generators
|
||||
{
|
||||
internal class ServiceSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public HashSet<ClassDeclarationSyntax> Types = new HashSet<ClassDeclarationSyntax>();
|
||||
|
||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax classDeclaration)
|
||||
{
|
||||
if (classDeclaration.BaseList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Types.Add(classDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,27 +8,24 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
static class AppletManager
|
||||
{
|
||||
private static readonly Dictionary<AppletId, Type> _appletMapping;
|
||||
|
||||
static AppletManager()
|
||||
{
|
||||
_appletMapping = new Dictionary<AppletId, Type>
|
||||
{
|
||||
{ AppletId.Error, typeof(ErrorApplet) },
|
||||
{ AppletId.PlayerSelect, typeof(PlayerSelectApplet) },
|
||||
{ AppletId.Controller, typeof(ControllerApplet) },
|
||||
{ AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) },
|
||||
{ AppletId.LibAppletWeb, typeof(BrowserApplet) },
|
||||
{ AppletId.LibAppletShop, typeof(BrowserApplet) },
|
||||
{ AppletId.LibAppletOff, typeof(BrowserApplet) },
|
||||
};
|
||||
}
|
||||
|
||||
public static IApplet Create(AppletId applet, Horizon system)
|
||||
{
|
||||
if (_appletMapping.TryGetValue(applet, out Type appletClass))
|
||||
switch (applet)
|
||||
{
|
||||
return (IApplet)Activator.CreateInstance(appletClass, system);
|
||||
case AppletId.Controller:
|
||||
return new ControllerApplet(system);
|
||||
case AppletId.Error:
|
||||
return new ErrorApplet(system);
|
||||
case AppletId.PlayerSelect:
|
||||
return new PlayerSelectApplet(system);
|
||||
case AppletId.SoftwareKeyboard:
|
||||
return new SoftwareKeyboardApplet(system);
|
||||
case AppletId.LibAppletWeb:
|
||||
return new BrowserApplet(system);
|
||||
case AppletId.LibAppletShop:
|
||||
return new BrowserApplet(system);
|
||||
case AppletId.LibAppletOff:
|
||||
return new BrowserApplet(system);
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"{applet} applet is not implemented.");
|
||||
|
@ -112,11 +112,16 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
// Update the parameters that were provided.
|
||||
_state.InputText = inputText ?? _state.InputText;
|
||||
_state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
|
||||
_state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
|
||||
_state.CursorBegin = Math.Max(0, cursorBegin.GetValueOrDefault(_state.CursorBegin));
|
||||
_state.CursorEnd = Math.Min(cursorEnd.GetValueOrDefault(_state.CursorEnd), _state.InputText.Length);
|
||||
_state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode);
|
||||
_state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled);
|
||||
|
||||
var begin = _state.CursorBegin;
|
||||
var end = _state.CursorEnd;
|
||||
_state.CursorBegin = Math.Min(begin, end);
|
||||
_state.CursorEnd = Math.Max(begin, end);
|
||||
|
||||
// Reset the cursor blink.
|
||||
_state.TextBoxBlinkCounter = 0;
|
||||
|
||||
|
@ -1,14 +1,9 @@
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Memory;
|
||||
using SixLabors.Fonts;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -29,38 +24,39 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
private readonly object _bufferLock = new();
|
||||
|
||||
private RenderingSurfaceInfo _surfaceInfo = null;
|
||||
private Image<Argb32> _surface = null;
|
||||
private SKImageInfo _imageInfo;
|
||||
private SKSurface _surface = null;
|
||||
private byte[] _bufferData = null;
|
||||
|
||||
private readonly Image _ryujinxLogo = null;
|
||||
private readonly Image _padAcceptIcon = null;
|
||||
private readonly Image _padCancelIcon = null;
|
||||
private readonly Image _keyModeIcon = null;
|
||||
private readonly SKBitmap _ryujinxLogo = null;
|
||||
private readonly SKBitmap _padAcceptIcon = null;
|
||||
private readonly SKBitmap _padCancelIcon = null;
|
||||
private readonly SKBitmap _keyModeIcon = null;
|
||||
|
||||
private readonly float _textBoxOutlineWidth;
|
||||
private readonly float _padPressedPenWidth;
|
||||
|
||||
private readonly Color _textNormalColor;
|
||||
private readonly Color _textSelectedColor;
|
||||
private readonly Color _textOverCursorColor;
|
||||
private readonly SKColor _textNormalColor;
|
||||
private readonly SKColor _textSelectedColor;
|
||||
private readonly SKColor _textOverCursorColor;
|
||||
|
||||
private readonly Brush _panelBrush;
|
||||
private readonly Brush _disabledBrush;
|
||||
private readonly Brush _cursorBrush;
|
||||
private readonly Brush _selectionBoxBrush;
|
||||
private readonly SKPaint _panelBrush;
|
||||
private readonly SKPaint _disabledBrush;
|
||||
private readonly SKPaint _cursorBrush;
|
||||
private readonly SKPaint _selectionBoxBrush;
|
||||
|
||||
private readonly Pen _textBoxOutlinePen;
|
||||
private readonly Pen _cursorPen;
|
||||
private readonly Pen _selectionBoxPen;
|
||||
private readonly Pen _padPressedPen;
|
||||
private readonly SKPaint _textBoxOutlinePen;
|
||||
private readonly SKPaint _cursorPen;
|
||||
private readonly SKPaint _selectionBoxPen;
|
||||
private readonly SKPaint _padPressedPen;
|
||||
|
||||
private readonly int _inputTextFontSize;
|
||||
private Font _messageFont;
|
||||
private Font _inputTextFont;
|
||||
private Font _labelsTextFont;
|
||||
private SKFont _messageFont;
|
||||
private SKFont _inputTextFont;
|
||||
private SKFont _labelsTextFont;
|
||||
|
||||
private RectangleF _panelRectangle;
|
||||
private Point _logoPosition;
|
||||
private SKRect _panelRectangle;
|
||||
private SKPoint _logoPosition;
|
||||
private float _messagePositionY;
|
||||
|
||||
public SoftwareKeyboardRendererBase(IHostUITheme uiTheme)
|
||||
@ -78,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
_padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0);
|
||||
_keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0);
|
||||
|
||||
Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
|
||||
Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
|
||||
Color borderColor = ToColor(uiTheme.DefaultBorderColor);
|
||||
Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
|
||||
var panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
|
||||
var panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
|
||||
var borderColor = ToColor(uiTheme.DefaultBorderColor);
|
||||
var selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
|
||||
|
||||
_textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
|
||||
_textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
|
||||
@ -92,15 +88,29 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
_textBoxOutlineWidth = 2;
|
||||
_padPressedPenWidth = 2;
|
||||
|
||||
_panelBrush = new SolidBrush(panelColor);
|
||||
_disabledBrush = new SolidBrush(panelTransparentColor);
|
||||
_cursorBrush = new SolidBrush(_textNormalColor);
|
||||
_selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
|
||||
_panelBrush = new SKPaint()
|
||||
{
|
||||
Color = panelColor,
|
||||
IsAntialias = true
|
||||
};
|
||||
_disabledBrush = new SKPaint()
|
||||
{
|
||||
Color = panelTransparentColor,
|
||||
IsAntialias = true
|
||||
};
|
||||
_cursorBrush = new SKPaint() { Color = _textNormalColor, IsAntialias = true };
|
||||
_selectionBoxBrush = new SKPaint() { Color = selectionBackgroundColor, IsAntialias = true };
|
||||
|
||||
_textBoxOutlinePen = Pens.Solid(borderColor, _textBoxOutlineWidth);
|
||||
_cursorPen = Pens.Solid(_textNormalColor, cursorWidth);
|
||||
_selectionBoxPen = Pens.Solid(selectionBackgroundColor, cursorWidth);
|
||||
_padPressedPen = Pens.Solid(borderColor, _padPressedPenWidth);
|
||||
_textBoxOutlinePen = new SKPaint()
|
||||
{
|
||||
Color = borderColor,
|
||||
StrokeWidth = _textBoxOutlineWidth,
|
||||
IsStroke = true,
|
||||
IsAntialias = true
|
||||
};
|
||||
_cursorPen = new SKPaint() { Color = _textNormalColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
|
||||
_selectionBoxPen = new SKPaint() { Color = selectionBackgroundColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
|
||||
_padPressedPen = new SKPaint() { Color = borderColor, StrokeWidth = _padPressedPenWidth, IsStroke = true, IsAntialias = true };
|
||||
|
||||
_inputTextFontSize = 20;
|
||||
|
||||
@ -123,9 +133,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
try
|
||||
{
|
||||
_messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
|
||||
_inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
|
||||
_labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
|
||||
using var typeface = SKTypeface.FromFamilyName(fontFamily, SKFontStyle.Normal);
|
||||
_messageFont = new SKFont(typeface, 26);
|
||||
_inputTextFont = new SKFont(typeface, _inputTextFontSize);
|
||||
_labelsTextFont = new SKFont(typeface, 24);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -137,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
|
||||
}
|
||||
|
||||
private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
|
||||
private static SKColor ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
|
||||
{
|
||||
var a = (byte)(color.A * 255);
|
||||
var r = (byte)(color.R * 255);
|
||||
@ -151,34 +162,33 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
b = (byte)(255 - b);
|
||||
}
|
||||
|
||||
return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a));
|
||||
return new SKColor(r, g, b, overrideAlpha.GetValueOrDefault(a));
|
||||
}
|
||||
|
||||
private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
|
||||
private static SKBitmap LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
|
||||
{
|
||||
Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
|
||||
|
||||
return LoadResource(resourceStream, newWidth, newHeight);
|
||||
}
|
||||
|
||||
private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight)
|
||||
private static SKBitmap LoadResource(Stream resourceStream, int newWidth, int newHeight)
|
||||
{
|
||||
Debug.Assert(resourceStream != null);
|
||||
|
||||
var image = Image.Load(resourceStream);
|
||||
var bitmap = SKBitmap.Decode(resourceStream);
|
||||
|
||||
if (newHeight != 0 && newWidth != 0)
|
||||
{
|
||||
image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
|
||||
var resized = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.High);
|
||||
if (resized != null)
|
||||
{
|
||||
bitmap.Dispose();
|
||||
bitmap = resized;
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static void SetGraphicsOptions(IImageProcessingContext context)
|
||||
{
|
||||
context.GetGraphicsOptions().Antialias = true;
|
||||
context.GetDrawingOptions().GraphicsOptions.Antialias = true;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private void DrawImmutableElements()
|
||||
@ -187,22 +197,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
return;
|
||||
}
|
||||
var canvas = _surface.Canvas;
|
||||
|
||||
_surface.Mutate(context =>
|
||||
{
|
||||
SetGraphicsOptions(context);
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
canvas.DrawRect(_panelRectangle, _panelBrush);
|
||||
canvas.DrawBitmap(_ryujinxLogo, _logoPosition);
|
||||
|
||||
context.Clear(Color.Transparent);
|
||||
context.Fill(_panelBrush, _panelRectangle);
|
||||
context.DrawImage(_ryujinxLogo, _logoPosition, 1);
|
||||
float halfWidth = _panelRectangle.Width / 2;
|
||||
float buttonsY = _panelRectangle.Top + 185;
|
||||
|
||||
float halfWidth = _panelRectangle.Width / 2;
|
||||
float buttonsY = _panelRectangle.Y + 185;
|
||||
SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
|
||||
|
||||
PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
|
||||
|
||||
DrawControllerToggle(context, disableButtonPosition);
|
||||
});
|
||||
DrawControllerToggle(canvas, disableButtonPosition);
|
||||
}
|
||||
|
||||
public void DrawMutableElements(SoftwareKeyboardUIState state)
|
||||
@ -212,40 +218,43 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
return;
|
||||
}
|
||||
|
||||
_surface.Mutate(context =>
|
||||
using var paint = new SKPaint(_messageFont)
|
||||
{
|
||||
var messageRectangle = MeasureString(MessageText, _messageFont);
|
||||
float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
|
||||
float messagePositionY = _messagePositionY - messageRectangle.Y;
|
||||
var messagePosition = new PointF(messagePositionX, messagePositionY);
|
||||
var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
|
||||
Color = _textNormalColor,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
SetGraphicsOptions(context);
|
||||
var canvas = _surface.Canvas;
|
||||
var messageRectangle = MeasureString(MessageText, paint);
|
||||
float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.Left;
|
||||
float messagePositionY = _messagePositionY - messageRectangle.Top;
|
||||
var messagePosition = new SKPoint(messagePositionX, messagePositionY);
|
||||
var messageBoundRectangle = SKRect.Create(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
|
||||
|
||||
context.Fill(_panelBrush, messageBoundRectangle);
|
||||
canvas.DrawRect(messageBoundRectangle, _panelBrush);
|
||||
|
||||
context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition);
|
||||
canvas.DrawText(MessageText, messagePosition.X, messagePosition.Y + _messageFont.Metrics.XHeight + _messageFont.Metrics.Descent, paint);
|
||||
|
||||
if (!state.TypingEnabled)
|
||||
{
|
||||
// Just draw a semi-transparent rectangle on top to fade the component with the background.
|
||||
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
|
||||
if (!state.TypingEnabled)
|
||||
{
|
||||
// Just draw a semi-transparent rectangle on top to fade the component with the background.
|
||||
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
|
||||
|
||||
context.Fill(_disabledBrush, messageBoundRectangle);
|
||||
}
|
||||
canvas.DrawRect(messageBoundRectangle, _disabledBrush);
|
||||
}
|
||||
|
||||
DrawTextBox(context, state);
|
||||
DrawTextBox(canvas, state);
|
||||
|
||||
float halfWidth = _panelRectangle.Width / 2;
|
||||
float buttonsY = _panelRectangle.Y + 185;
|
||||
float halfWidth = _panelRectangle.Width / 2;
|
||||
float buttonsY = _panelRectangle.Top + 185;
|
||||
|
||||
PointF acceptButtonPosition = new(halfWidth - 180, buttonsY);
|
||||
PointF cancelButtonPosition = new(halfWidth, buttonsY);
|
||||
PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
|
||||
SKPoint acceptButtonPosition = new(halfWidth - 180, buttonsY);
|
||||
SKPoint cancelButtonPosition = new(halfWidth, buttonsY);
|
||||
SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
|
||||
|
||||
DrawPadButton(canvas, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
|
||||
DrawPadButton(canvas, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
|
||||
|
||||
DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
|
||||
DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
public void CreateSurface(RenderingSurfaceInfo surfaceInfo)
|
||||
@ -268,7 +277,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
Debug.Assert(_surfaceInfo.Height <= totalHeight);
|
||||
Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size);
|
||||
|
||||
_surface = new Image<Argb32>((int)totalWidth, (int)totalHeight);
|
||||
_imageInfo = new SKImageInfo((int)totalWidth, (int)totalHeight, SKColorType.Rgba8888);
|
||||
_surface = SKSurface.Create(_imageInfo);
|
||||
|
||||
ComputeConstants();
|
||||
DrawImmutableElements();
|
||||
@ -282,76 +292,81 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
int panelHeight = 240;
|
||||
int panelPositionY = totalHeight - panelHeight;
|
||||
|
||||
_panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight);
|
||||
_panelRectangle = SKRect.Create(0, panelPositionY, totalWidth, panelHeight);
|
||||
|
||||
_messagePositionY = panelPositionY + 60;
|
||||
|
||||
int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2;
|
||||
int logoPositionY = panelPositionY + 18;
|
||||
|
||||
_logoPosition = new Point(logoPositionX, logoPositionY);
|
||||
_logoPosition = new SKPoint(logoPositionX, logoPositionY);
|
||||
}
|
||||
private static RectangleF MeasureString(string text, Font font)
|
||||
private static SKRect MeasureString(string text, SKPaint paint)
|
||||
{
|
||||
TextOptions options = new(font);
|
||||
SKRect bounds = SKRect.Empty;
|
||||
|
||||
if (text == "")
|
||||
{
|
||||
FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
|
||||
|
||||
return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
|
||||
paint.MeasureText(" ", ref bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
paint.MeasureText(text, ref bounds);
|
||||
}
|
||||
|
||||
FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
|
||||
|
||||
return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private static RectangleF MeasureString(ReadOnlySpan<char> text, Font font)
|
||||
private static SKRect MeasureString(ReadOnlySpan<char> text, SKPaint paint)
|
||||
{
|
||||
TextOptions options = new(font);
|
||||
SKRect bounds = SKRect.Empty;
|
||||
|
||||
if (text == "")
|
||||
{
|
||||
FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
|
||||
return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
|
||||
paint.MeasureText(" ", ref bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
paint.MeasureText(text, ref bounds);
|
||||
}
|
||||
|
||||
FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
|
||||
|
||||
return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUIState state)
|
||||
private void DrawTextBox(SKCanvas canvas, SoftwareKeyboardUIState state)
|
||||
{
|
||||
var inputTextRectangle = MeasureString(state.InputText, _inputTextFont);
|
||||
using var textPaint = new SKPaint(_labelsTextFont)
|
||||
{
|
||||
IsAntialias = true,
|
||||
Color = _textNormalColor
|
||||
};
|
||||
var inputTextRectangle = MeasureString(state.InputText, textPaint);
|
||||
|
||||
float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
|
||||
float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.Left + 8));
|
||||
float boxHeight = 32;
|
||||
float boxY = _panelRectangle.Y + 110;
|
||||
float boxY = _panelRectangle.Top + 110;
|
||||
float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
|
||||
|
||||
RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight);
|
||||
SKRect boxRectangle = SKRect.Create(boxX, boxY, boxWidth, boxHeight);
|
||||
|
||||
RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth,
|
||||
SKRect boundRectangle = SKRect.Create(_panelRectangle.Left, boxY - _textBoxOutlineWidth,
|
||||
_panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth);
|
||||
|
||||
context.Fill(_panelBrush, boundRectangle);
|
||||
canvas.DrawRect(boundRectangle, _panelBrush);
|
||||
|
||||
context.Draw(_textBoxOutlinePen, boxRectangle);
|
||||
canvas.DrawRect(boxRectangle, _textBoxOutlinePen);
|
||||
|
||||
float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X;
|
||||
float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.Left;
|
||||
float inputTextY = boxY + 5;
|
||||
|
||||
var inputTextPosition = new PointF(inputTextX, inputTextY);
|
||||
|
||||
context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition);
|
||||
var inputTextPosition = new SKPoint(inputTextX, inputTextY);
|
||||
canvas.DrawText(state.InputText, inputTextPosition.X, inputTextPosition.Y + (_labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent), textPaint);
|
||||
|
||||
// Draw the cursor on top of the text and redraw the text with a different color if necessary.
|
||||
|
||||
Color cursorTextColor;
|
||||
Brush cursorBrush;
|
||||
Pen cursorPen;
|
||||
SKColor cursorTextColor;
|
||||
SKPaint cursorBrush;
|
||||
SKPaint cursorPen;
|
||||
|
||||
float cursorPositionYTop = inputTextY + 1;
|
||||
float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1;
|
||||
@ -371,12 +386,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
ReadOnlySpan<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin);
|
||||
ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd);
|
||||
|
||||
var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont);
|
||||
var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont);
|
||||
var selectionBeginRectangle = MeasureString(textUntilBegin, textPaint);
|
||||
var selectionEndRectangle = MeasureString(textUntilEnd, textPaint);
|
||||
|
||||
cursorVisible = true;
|
||||
cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
|
||||
cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
|
||||
cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.Left;
|
||||
cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -390,10 +405,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
|
||||
int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
|
||||
ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin);
|
||||
var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
|
||||
var cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
|
||||
|
||||
cursorVisible = true;
|
||||
cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
|
||||
cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
|
||||
|
||||
if (state.OverwriteMode)
|
||||
{
|
||||
@ -402,8 +417,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
if (state.CursorBegin < state.InputText.Length)
|
||||
{
|
||||
textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1);
|
||||
cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
|
||||
cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
|
||||
cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
|
||||
cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -430,29 +445,32 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
|
||||
if (cursorWidth == 0)
|
||||
{
|
||||
PointF[] points = {
|
||||
new PointF(cursorPositionXLeft, cursorPositionYTop),
|
||||
new PointF(cursorPositionXLeft, cursorPositionYBottom),
|
||||
};
|
||||
|
||||
context.DrawLine(cursorPen, points);
|
||||
canvas.DrawLine(new SKPoint(cursorPositionXLeft, cursorPositionYTop),
|
||||
new SKPoint(cursorPositionXLeft, cursorPositionYBottom),
|
||||
cursorPen);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
|
||||
var cursorRectangle = SKRect.Create(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
|
||||
|
||||
context.Draw(cursorPen, cursorRectangle);
|
||||
context.Fill(cursorBrush, cursorRectangle);
|
||||
canvas.DrawRect(cursorRectangle, cursorPen);
|
||||
canvas.DrawRect(cursorRectangle, cursorBrush);
|
||||
|
||||
Image<Argb32> textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height);
|
||||
textOverCursor.Mutate(context =>
|
||||
using var textOverCursor = SKSurface.Create(new SKImageInfo((int)cursorRectangle.Width, (int)cursorRectangle.Height, SKColorType.Rgba8888));
|
||||
var textOverCanvas = textOverCursor.Canvas;
|
||||
var textRelativePosition = new SKPoint(inputTextPosition.X - cursorRectangle.Left, inputTextPosition.Y - cursorRectangle.Top);
|
||||
|
||||
using var cursorPaint = new SKPaint(_inputTextFont)
|
||||
{
|
||||
var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y);
|
||||
context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition);
|
||||
});
|
||||
Color = cursorTextColor,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y);
|
||||
context.DrawImage(textOverCursor, cursorPosition, 1);
|
||||
textOverCanvas.DrawText(state.InputText, textRelativePosition.X, textRelativePosition.Y + _inputTextFont.Metrics.XHeight + _inputTextFont.Metrics.Descent, cursorPaint);
|
||||
|
||||
var cursorPosition = new SKPoint((int)cursorRectangle.Left, (int)cursorRectangle.Top);
|
||||
textOverCursor.Flush();
|
||||
canvas.DrawSurface(textOverCursor, cursorPosition);
|
||||
}
|
||||
}
|
||||
else if (!state.TypingEnabled)
|
||||
@ -460,11 +478,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
// Just draw a semi-transparent rectangle on top to fade the component with the background.
|
||||
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
|
||||
|
||||
context.Fill(_disabledBrush, boundRectangle);
|
||||
canvas.DrawRect(boundRectangle, _disabledBrush);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled)
|
||||
private void DrawPadButton(SKCanvas canvas, SKPoint point, SKBitmap icon, string label, bool pressed, bool enabled)
|
||||
{
|
||||
// Use relative positions so we can center the entire drawing later.
|
||||
|
||||
@ -473,12 +491,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
float iconWidth = icon.Width;
|
||||
float iconHeight = icon.Height;
|
||||
|
||||
var labelRectangle = MeasureString(label, _labelsTextFont);
|
||||
using var paint = new SKPaint(_labelsTextFont)
|
||||
{
|
||||
Color = _textNormalColor,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
float labelPositionX = iconWidth + 8 - labelRectangle.X;
|
||||
var labelRectangle = MeasureString(label, paint);
|
||||
|
||||
float labelPositionX = iconWidth + 8 - labelRectangle.Left;
|
||||
float labelPositionY = 3;
|
||||
|
||||
float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
|
||||
float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.Left;
|
||||
float fullHeight = iconHeight;
|
||||
|
||||
// Convert all relative positions into absolute.
|
||||
@ -489,24 +513,24 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
iconX += originX;
|
||||
iconY += originY;
|
||||
|
||||
var iconPosition = new Point((int)iconX, (int)iconY);
|
||||
var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
|
||||
var iconPosition = new SKPoint((int)iconX, (int)iconY);
|
||||
var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
|
||||
|
||||
var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
|
||||
var selectedRectangle = SKRect.Create(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
|
||||
fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth);
|
||||
|
||||
var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight);
|
||||
var boundRectangle = SKRect.Create(originX, originY, fullWidth, fullHeight);
|
||||
boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth);
|
||||
|
||||
context.Fill(_panelBrush, boundRectangle);
|
||||
context.DrawImage(icon, iconPosition, 1);
|
||||
context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition);
|
||||
canvas.DrawRect(boundRectangle, _panelBrush);
|
||||
canvas.DrawBitmap(icon, iconPosition);
|
||||
canvas.DrawText(label, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent, paint);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
if (pressed)
|
||||
{
|
||||
context.Draw(_padPressedPen, selectedRectangle);
|
||||
canvas.DrawRect(selectedRectangle, _padPressedPen);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -514,21 +538,26 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
// Just draw a semi-transparent rectangle on top to fade the component with the background.
|
||||
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
|
||||
|
||||
context.Fill(_disabledBrush, boundRectangle);
|
||||
canvas.DrawRect(boundRectangle, _disabledBrush);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawControllerToggle(IImageProcessingContext context, PointF point)
|
||||
private void DrawControllerToggle(SKCanvas canvas, SKPoint point)
|
||||
{
|
||||
var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont);
|
||||
using var paint = new SKPaint(_labelsTextFont)
|
||||
{
|
||||
IsAntialias = true,
|
||||
Color = _textNormalColor
|
||||
};
|
||||
var labelRectangle = MeasureString(ControllerToggleText, paint);
|
||||
|
||||
// Use relative positions so we can center the entire drawing later.
|
||||
|
||||
float keyWidth = _keyModeIcon.Width;
|
||||
float keyHeight = _keyModeIcon.Height;
|
||||
|
||||
float labelPositionX = keyWidth + 8 - labelRectangle.X;
|
||||
float labelPositionY = -labelRectangle.Y - 1;
|
||||
float labelPositionX = keyWidth + 8 - labelRectangle.Left;
|
||||
float labelPositionY = -labelRectangle.Top - 1;
|
||||
|
||||
float keyX = 0;
|
||||
float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
|
||||
@ -544,14 +573,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
keyX += originX;
|
||||
keyY += originY;
|
||||
|
||||
var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
|
||||
var overlayPosition = new Point((int)keyX, (int)keyY);
|
||||
var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
|
||||
var overlayPosition = new SKPoint((int)keyX, (int)keyY);
|
||||
|
||||
context.DrawImage(_keyModeIcon, overlayPosition, 1);
|
||||
context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition);
|
||||
canvas.DrawBitmap(_keyModeIcon, overlayPosition);
|
||||
canvas.DrawText(ControllerToggleText, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight, paint);
|
||||
}
|
||||
|
||||
public void CopyImageToBuffer()
|
||||
public unsafe void CopyImageToBuffer()
|
||||
{
|
||||
lock (_bufferLock)
|
||||
{
|
||||
@ -561,21 +590,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
}
|
||||
|
||||
// Convert the pixel format used in the image to the one used in the Switch surface.
|
||||
_surface.Flush();
|
||||
|
||||
if (!_surface.DangerousTryGetSinglePixelMemory(out Memory<Argb32> pixels))
|
||||
var buffer = new byte[_imageInfo.BytesSize];
|
||||
fixed (byte* bufferPtr = buffer)
|
||||
{
|
||||
return;
|
||||
if (!_surface.ReadPixels(_imageInfo, (nint)bufferPtr, _imageInfo.RowBytes, 0, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray();
|
||||
Span<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(_bufferData);
|
||||
_bufferData = buffer;
|
||||
|
||||
Debug.Assert(_bufferData.Length == _surfaceInfo.Size);
|
||||
|
||||
for (int i = 0; i < dataConvert.Length; i++)
|
||||
{
|
||||
dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8);
|
||||
}
|
||||
Debug.Assert(buffer.Length == _surfaceInfo.Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
enum AddressSpaceType
|
||||
{
|
||||
Addr32Bits = 0,
|
||||
Addr36Bits = 1,
|
||||
Addr32BitsNoMap = 2,
|
||||
Addr39Bits = 3,
|
||||
}
|
||||
}
|
@ -58,11 +58,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
public ulong AslrRegionStart { get; private set; }
|
||||
public ulong AslrRegionEnd { get; private set; }
|
||||
|
||||
#pragma warning disable IDE0052 // Remove unread private member
|
||||
private ulong _heapCapacity;
|
||||
#pragma warning restore IDE0052
|
||||
|
||||
public ulong PhysicalMemoryUsage { get; private set; }
|
||||
public ulong AliasRegionExtraSize { get; private set; }
|
||||
|
||||
private readonly KMemoryBlockManager _blockManager;
|
||||
|
||||
@ -98,30 +97,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_reservedAddressSpaceSize = reservedAddressSpaceSize;
|
||||
}
|
||||
|
||||
private static readonly int[] _addrSpaceSizes = { 32, 36, 32, 39 };
|
||||
|
||||
public Result InitializeForProcess(
|
||||
AddressSpaceType addrSpaceType,
|
||||
bool aslrEnabled,
|
||||
ProcessCreationFlags flags,
|
||||
bool fromBack,
|
||||
MemoryRegion memRegion,
|
||||
ulong address,
|
||||
ulong size,
|
||||
KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
if ((uint)addrSpaceType > (uint)AddressSpaceType.Addr39Bits)
|
||||
{
|
||||
throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType));
|
||||
}
|
||||
|
||||
_contextId = Context.ContextIdManager.GetId();
|
||||
|
||||
ulong addrSpaceBase = 0;
|
||||
ulong addrSpaceSize = 1UL << _addrSpaceSizes[(int)addrSpaceType];
|
||||
ulong addrSpaceSize = 1UL << GetAddressSpaceWidth(flags);
|
||||
|
||||
Result result = CreateUserAddressSpace(
|
||||
addrSpaceType,
|
||||
aslrEnabled,
|
||||
flags,
|
||||
fromBack,
|
||||
addrSpaceBase,
|
||||
addrSpaceSize,
|
||||
@ -138,6 +128,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int GetAddressSpaceWidth(ProcessCreationFlags flags)
|
||||
{
|
||||
switch (flags & ProcessCreationFlags.AddressSpaceMask)
|
||||
{
|
||||
case ProcessCreationFlags.AddressSpace32Bit:
|
||||
case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
|
||||
return 32;
|
||||
case ProcessCreationFlags.AddressSpace64BitDeprecated:
|
||||
return 36;
|
||||
case ProcessCreationFlags.AddressSpace64Bit:
|
||||
return 39;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid process flags {flags}", nameof(flags));
|
||||
}
|
||||
|
||||
private struct Region
|
||||
{
|
||||
public ulong Start;
|
||||
@ -147,8 +153,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
}
|
||||
|
||||
private Result CreateUserAddressSpace(
|
||||
AddressSpaceType addrSpaceType,
|
||||
bool aslrEnabled,
|
||||
ProcessCreationFlags flags,
|
||||
bool fromBack,
|
||||
ulong addrSpaceStart,
|
||||
ulong addrSpaceEnd,
|
||||
@ -168,9 +173,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
ulong stackAndTlsIoStart;
|
||||
ulong stackAndTlsIoEnd;
|
||||
|
||||
switch (addrSpaceType)
|
||||
AliasRegionExtraSize = 0;
|
||||
|
||||
switch (flags & ProcessCreationFlags.AddressSpaceMask)
|
||||
{
|
||||
case AddressSpaceType.Addr32Bits:
|
||||
case ProcessCreationFlags.AddressSpace32Bit:
|
||||
aliasRegion.Size = 0x40000000;
|
||||
heapRegion.Size = 0x40000000;
|
||||
stackRegion.Size = 0;
|
||||
@ -183,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
|
||||
case AddressSpaceType.Addr36Bits:
|
||||
case ProcessCreationFlags.AddressSpace64BitDeprecated:
|
||||
aliasRegion.Size = 0x180000000;
|
||||
heapRegion.Size = 0x180000000;
|
||||
stackRegion.Size = 0;
|
||||
@ -196,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
stackAndTlsIoEnd = 0x80000000;
|
||||
break;
|
||||
|
||||
case AddressSpaceType.Addr32BitsNoMap:
|
||||
case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
|
||||
aliasRegion.Size = 0;
|
||||
heapRegion.Size = 0x80000000;
|
||||
stackRegion.Size = 0;
|
||||
@ -209,7 +216,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
|
||||
case AddressSpaceType.Addr39Bits:
|
||||
case ProcessCreationFlags.AddressSpace64Bit:
|
||||
if (_reservedAddressSpaceSize < addrSpaceEnd)
|
||||
{
|
||||
int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize);
|
||||
@ -218,8 +225,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
heapRegion.Size = 0x180000000;
|
||||
stackRegion.Size = 1UL << (addressSpaceWidth - 8);
|
||||
tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3);
|
||||
CodeRegionStart = BitUtils.AlignDown<ulong>(address, RegionAlignment);
|
||||
codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment);
|
||||
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
AslrRegionStart = 0x8000000;
|
||||
@ -239,9 +246,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(ProcessCreationFlags.EnableAliasRegionExtraSize))
|
||||
{
|
||||
AliasRegionExtraSize = addrSpaceEnd / 8;
|
||||
aliasRegion.Size += AliasRegionExtraSize;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType));
|
||||
throw new ArgumentException($"Invalid process flags {flags}", nameof(flags));
|
||||
}
|
||||
|
||||
CodeRegionEnd = CodeRegionStart + codeRegionSize;
|
||||
@ -266,6 +280,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
ulong aslrMaxOffset = mapAvailableSize - mapTotalSize;
|
||||
|
||||
bool aslrEnabled = flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||
|
||||
_aslrEnabled = aslrEnabled;
|
||||
|
||||
AddrSpaceStart = addrSpaceStart;
|
||||
@ -725,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
address = 0;
|
||||
|
||||
if (size > HeapRegionEnd - HeapRegionStart)
|
||||
if (size > HeapRegionEnd - HeapRegionStart || size > _heapCapacity)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
@ -126,8 +126,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
_contextFactory = contextFactory ?? new ProcessContextFactory();
|
||||
_customThreadStart = customThreadStart;
|
||||
|
||||
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
||||
|
||||
Pid = KernelContext.NewKipId();
|
||||
|
||||
if (Pid == 0 || Pid >= KernelConstants.InitialProcessId)
|
||||
@ -137,8 +135,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
InitializeMemoryManager(creationInfo.Flags);
|
||||
|
||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
|
||||
ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
|
||||
@ -148,9 +144,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
: KernelContext.SmallMemoryBlockSlabManager;
|
||||
|
||||
Result result = MemoryManager.InitializeForProcess(
|
||||
addrSpaceType,
|
||||
aslrEnabled,
|
||||
!aslrEnabled,
|
||||
creationInfo.Flags,
|
||||
!creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr),
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
@ -234,8 +229,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
: KernelContext.SmallMemoryBlockSlabManager;
|
||||
}
|
||||
|
||||
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
||||
|
||||
Pid = KernelContext.NewProcessId();
|
||||
|
||||
if (Pid == ulong.MaxValue || Pid < KernelConstants.InitialProcessId)
|
||||
@ -245,16 +238,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
InitializeMemoryManager(creationInfo.Flags);
|
||||
|
||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
|
||||
ulong codeSize = codePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
Result result = MemoryManager.InitializeForProcess(
|
||||
addrSpaceType,
|
||||
aslrEnabled,
|
||||
!aslrEnabled,
|
||||
creationInfo.Flags,
|
||||
!creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr),
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
@ -309,8 +299,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
private Result ParseProcessInfo(ProcessCreationInfo creationInfo)
|
||||
{
|
||||
// Ensure that the current kernel version is equal or above to the minimum required.
|
||||
uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
|
||||
uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
|
||||
uint requiredKernelVersionMajor = Capabilities.KernelReleaseVersion >> 19;
|
||||
uint requiredKernelVersionMinor = (Capabilities.KernelReleaseVersion >> 15) & 0xf;
|
||||
|
||||
if (KernelContext.EnableVersionChecks)
|
||||
{
|
||||
@ -519,12 +509,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
private void GenerateRandomEntropy()
|
||||
{
|
||||
// TODO.
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
public Result Start(int mainThreadPriority, ulong stackSize)
|
||||
{
|
||||
@ -1182,5 +1170,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsSvcPermitted(int svcId)
|
||||
{
|
||||
return Capabilities.IsSvcPermitted(svcId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
class KProcessCapabilities
|
||||
{
|
||||
private const int SvcMaskElementBits = 8;
|
||||
|
||||
public byte[] SvcAccessMask { get; }
|
||||
public byte[] IrqAccessMask { get; }
|
||||
|
||||
@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
public KProcessCapabilities()
|
||||
{
|
||||
// length / number of bits of the underlying type
|
||||
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
|
||||
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / SvcMaskElementBits];
|
||||
IrqAccessMask = new byte[0x80];
|
||||
}
|
||||
|
||||
@ -208,7 +210,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
return KernelResult.MaximumExceeded;
|
||||
}
|
||||
|
||||
SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7));
|
||||
SvcAccessMask[svcId / SvcMaskElementBits] |= (byte)(1 << (svcId % SvcMaskElementBits));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -324,5 +326,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
return mask << (int)min;
|
||||
}
|
||||
|
||||
public bool IsSvcPermitted(int svcId)
|
||||
{
|
||||
int index = svcId / SvcMaskElementBits;
|
||||
int mask = 1 << (svcId % SvcMaskElementBits);
|
||||
|
||||
return (uint)svcId < KernelConstants.SupervisorCallCount && (SvcAccessMask[index] & mask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
PoolPartitionMask = 0xf << PoolPartitionShift,
|
||||
|
||||
OptimizeMemoryAllocation = 1 << 11,
|
||||
DisableDeviceAddressSpaceMerge = 1 << 12,
|
||||
EnableAliasRegionExtraSize = 1 << 13,
|
||||
|
||||
All =
|
||||
Is64Bit |
|
||||
@ -38,6 +40,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
IsApplication |
|
||||
DeprecatedUseSecureMemory |
|
||||
PoolPartitionMask |
|
||||
OptimizeMemoryAllocation,
|
||||
OptimizeMemoryAllocation |
|
||||
DisableDeviceAddressSpaceMerge |
|
||||
EnableAliasRegionExtraSize,
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
SystemResourceSizeTotal,
|
||||
SystemResourceSizeUsed,
|
||||
ProgramId,
|
||||
// NOTE: Added in 4.0.0, removed in 5.0.0.
|
||||
InitialProcessIdRange,
|
||||
InitialProcessIdRange, // NOTE: Added in 4.0.0, removed in 5.0.0.
|
||||
UserExceptionContextAddress,
|
||||
TotalNonSystemMemorySize,
|
||||
UsedNonSystemMemorySize,
|
||||
IsApplication,
|
||||
FreeThreadCount,
|
||||
ThreadTickCount,
|
||||
IsSvcPermitted,
|
||||
IoRegionHint,
|
||||
AliasRegionExtraSize,
|
||||
|
||||
MesosphereCurrentProcess = 65001,
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
if (info.Flags.HasFlag(ProcessCreationFlags.EnableAliasRegionExtraSize))
|
||||
{
|
||||
if ((info.Flags & ProcessCreationFlags.AddressSpaceMask) != ProcessCreationFlags.AddressSpace64Bit ||
|
||||
info.SystemResourcePagesCount <= 0)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
// TODO: Check that we are in debug mode.
|
||||
}
|
||||
|
||||
if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) &&
|
||||
!info.Flags.HasFlag(ProcessCreationFlags.IsApplication))
|
||||
{
|
||||
@ -139,7 +150,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return handleTable.GenerateHandle(process, out handle);
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
|
||||
@ -172,17 +182,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x5f)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result FlushProcessDataCache(int processHandle, ulong address, ulong size)
|
||||
{
|
||||
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
|
||||
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
// IPC
|
||||
|
||||
@ -256,7 +263,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x22)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SendSyncRequestWithUserBuffer(
|
||||
[PointerSized] ulong messagePtr,
|
||||
[PointerSized] ulong messageSize,
|
||||
@ -306,7 +312,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x23)]
|
||||
public Result SendAsyncRequestWithUserBuffer(
|
||||
@ -896,7 +901,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(2)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -928,10 +932,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(3)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetMemoryAttribute(
|
||||
[PointerSized] ulong address,
|
||||
[PointerSized] ulong size,
|
||||
@ -979,10 +981,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(4)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(src | dst))
|
||||
@ -1018,10 +1018,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return process.MemoryManager.Map(dst, src, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(5)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(src | dst))
|
||||
@ -1057,7 +1055,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return process.MemoryManager.Unmap(dst, src, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(6)]
|
||||
public Result QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address)
|
||||
@ -1074,7 +1071,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -1094,10 +1090,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x13)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1143,10 +1137,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
currentProcess,
|
||||
permission);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x14)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1186,7 +1178,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
size,
|
||||
currentProcess);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x15)]
|
||||
public Result CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||
@ -1253,7 +1244,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x51)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1299,10 +1289,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
currentProcess,
|
||||
permission);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x52)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1342,10 +1330,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
size,
|
||||
currentProcess);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x2c)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1380,10 +1366,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return process.MemoryManager.MapPhysicalMemory(address, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x2d)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
@ -1418,7 +1402,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return process.MemoryManager.UnmapPhysicalMemory(address, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x4b)]
|
||||
public Result CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||
@ -1462,7 +1445,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x4c)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result ControlCodeMemory(
|
||||
int handle,
|
||||
CodeMemoryOperation op,
|
||||
@ -1540,10 +1522,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidEnumValue;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x73)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetProcessMemoryPermission(
|
||||
int handle,
|
||||
ulong src,
|
||||
@ -1584,10 +1564,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x74)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapProcessMemory(
|
||||
[PointerSized] ulong dst,
|
||||
int handle,
|
||||
@ -1643,10 +1621,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x75)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapProcessMemory(
|
||||
[PointerSized] ulong dst,
|
||||
int handle,
|
||||
@ -1691,10 +1667,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x77)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||
{
|
||||
if (!PageAligned(dst) || !PageAligned(src))
|
||||
@ -1731,10 +1705,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x78)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||
{
|
||||
if (!PageAligned(dst) || !PageAligned(src))
|
||||
@ -1771,7 +1743,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
private static bool PageAligned(ulong address)
|
||||
{
|
||||
@ -1781,7 +1752,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
// System
|
||||
|
||||
[Svc(0x7b)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result TerminateProcess(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -1810,15 +1780,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(7)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public void ExitProcess()
|
||||
{
|
||||
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x11)]
|
||||
public Result SignalEvent(int handle)
|
||||
@ -1911,7 +1878,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x26)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public void Break(ulong reason)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
@ -1937,10 +1903,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
Logger.Debug?.Print(LogClass.KernelSvc, "Debugger triggered.");
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x27)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -1949,7 +1913,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
Logger.Warning?.Print(LogClass.KernelSvc, str);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x29)]
|
||||
public Result GetInfo(out ulong value, InfoType id, int handle, long subId)
|
||||
@ -1978,6 +1941,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
case InfoType.UsedNonSystemMemorySize:
|
||||
case InfoType.IsApplication:
|
||||
case InfoType.FreeThreadCount:
|
||||
case InfoType.AliasRegionExtraSize:
|
||||
{
|
||||
if (subId != 0)
|
||||
{
|
||||
@ -2006,22 +1970,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
value = process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
case InfoType.AliasRegionSize:
|
||||
value = (process.MemoryManager.AliasRegionEnd -
|
||||
process.MemoryManager.AliasRegionStart);
|
||||
value = process.MemoryManager.AliasRegionEnd - process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
case InfoType.HeapRegionAddress:
|
||||
value = process.MemoryManager.HeapRegionStart;
|
||||
break;
|
||||
case InfoType.HeapRegionSize:
|
||||
value = (process.MemoryManager.HeapRegionEnd -
|
||||
process.MemoryManager.HeapRegionStart);
|
||||
value = process.MemoryManager.HeapRegionEnd - process.MemoryManager.HeapRegionStart;
|
||||
break;
|
||||
|
||||
case InfoType.TotalMemorySize:
|
||||
value = process.GetMemoryCapacity();
|
||||
break;
|
||||
|
||||
case InfoType.UsedMemorySize:
|
||||
value = process.GetMemoryUsage();
|
||||
break;
|
||||
@ -2029,7 +1990,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
case InfoType.AslrRegionAddress:
|
||||
value = process.MemoryManager.GetAddrSpaceBaseAddr();
|
||||
break;
|
||||
|
||||
case InfoType.AslrRegionSize:
|
||||
value = process.MemoryManager.GetAddrSpaceSize();
|
||||
break;
|
||||
@ -2038,20 +1998,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
value = process.MemoryManager.StackRegionStart;
|
||||
break;
|
||||
case InfoType.StackRegionSize:
|
||||
value = (process.MemoryManager.StackRegionEnd -
|
||||
process.MemoryManager.StackRegionStart);
|
||||
value = process.MemoryManager.StackRegionEnd - process.MemoryManager.StackRegionStart;
|
||||
break;
|
||||
|
||||
case InfoType.SystemResourceSizeTotal:
|
||||
value = process.PersonalMmHeapPagesCount * KPageTableBase.PageSize;
|
||||
break;
|
||||
|
||||
case InfoType.SystemResourceSizeUsed:
|
||||
if (process.PersonalMmHeapPagesCount != 0)
|
||||
{
|
||||
value = process.MemoryManager.GetMmUsedPages() * KPageTableBase.PageSize;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case InfoType.ProgramId:
|
||||
@ -2065,7 +2022,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
case InfoType.TotalNonSystemMemorySize:
|
||||
value = process.GetMemoryCapacityWithoutPersonalMmHeap();
|
||||
break;
|
||||
|
||||
case InfoType.UsedNonSystemMemorySize:
|
||||
value = process.GetMemoryUsageWithoutPersonalMmHeap();
|
||||
break;
|
||||
@ -2084,10 +2040,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case InfoType.AliasRegionExtraSize:
|
||||
value = process.MemoryManager.AliasRegionExtraSize;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2104,7 +2062,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
value = KernelStatic.GetCurrentProcess().Debug ? 1UL : 0UL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2136,7 +2093,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
value = (uint)resLimHandle;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2155,7 +2111,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
value = (ulong)KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2174,7 +2129,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
|
||||
value = currentProcess.RandomEntropy[subId];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2220,7 +2174,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
value = (ulong)KTimeManager.ConvertHostTicksToTicks(totalTimeRunning);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case InfoType.IsSvcPermitted:
|
||||
{
|
||||
if (handle != 0)
|
||||
{
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
if (subId != 0x36)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
value = KernelStatic.GetCurrentProcess().IsSvcPermitted((int)subId) ? 1UL : 0UL;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2231,7 +2200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
if ((ulong)subId != 0)
|
||||
if (subId != 0)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
@ -2246,8 +2215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
value = (ulong)outHandle;
|
||||
|
||||
value = (uint)outHandle;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2398,7 +2366,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x30)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
|
||||
{
|
||||
limitValue = 0;
|
||||
@ -2419,10 +2386,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x31)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
|
||||
{
|
||||
limitValue = 0;
|
||||
@ -2443,10 +2408,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x37)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
|
||||
{
|
||||
peak = 0;
|
||||
@ -2467,7 +2430,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x7d)]
|
||||
public Result CreateResourceLimit(out int handle)
|
||||
@ -2480,7 +2442,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x7e)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
|
||||
{
|
||||
if (resource >= LimitableResource.Count)
|
||||
@ -2497,7 +2458,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return resourceLimit.SetLimitValue(resource, limitValue);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
// Thread
|
||||
|
||||
@ -2577,7 +2537,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(9)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result StartThread(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2604,17 +2563,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0xa)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public void ExitThread()
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
currentThread.Exit();
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0xb)]
|
||||
public void SleepThread(long timeout)
|
||||
@ -2641,7 +2597,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0xc)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetThreadPriority(out int priority, int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2661,10 +2616,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0xd)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetThreadPriority(int handle, int priority)
|
||||
{
|
||||
// TODO: NPDM check.
|
||||
@ -2682,10 +2635,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0xe)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2707,10 +2658,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0xf)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -2758,18 +2707,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x10)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public int GetCurrentProcessorNumber()
|
||||
{
|
||||
return KernelStatic.GetCurrentThread().CurrentCore;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x25)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetThreadId(out ulong threadUid, int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2789,10 +2734,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x32)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SetThreadActivity(int handle, bool pause)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2816,10 +2759,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return thread.SetActivity(pause);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x33)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result GetThreadContext3([PointerSized] ulong address, int handle)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -2853,7 +2794,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
// Thread synchronization
|
||||
|
||||
@ -2986,7 +2926,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
|
||||
[Svc(0x1a)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle)
|
||||
{
|
||||
if (IsPointingInsideKernel(mutexAddress))
|
||||
@ -3003,10 +2942,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x1b)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result ArbitrateUnlock([PointerSized] ulong mutexAddress)
|
||||
{
|
||||
if (IsPointingInsideKernel(mutexAddress))
|
||||
@ -3023,10 +2960,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x1c)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result WaitProcessWideKeyAtomic(
|
||||
[PointerSized] ulong mutexAddress,
|
||||
[PointerSized] ulong condVarAddress,
|
||||
@ -3056,10 +2991,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
handle,
|
||||
timeout);
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x1d)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SignalProcessWideKey([PointerSized] ulong address, int count)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -3068,10 +3001,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x34)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout)
|
||||
{
|
||||
if (IsPointingInsideKernel(address))
|
||||
@ -3102,10 +3033,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
_ => KernelResult.InvalidEnumValue,
|
||||
};
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x35)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count)
|
||||
{
|
||||
if (IsPointingInsideKernel(address))
|
||||
@ -3131,17 +3060,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
_ => KernelResult.InvalidEnumValue,
|
||||
};
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
[Svc(0x36)]
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public Result SynchronizePreemptionState()
|
||||
{
|
||||
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
// Not actual syscalls, used by HLE services and such.
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Caps
|
||||
@ -118,7 +118,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
||||
}
|
||||
|
||||
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
|
||||
Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(1280, 720, SKColorType.Rgba8888));
|
||||
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
|
||||
using var data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||
using var file = File.OpenWrite(filePath);
|
||||
data.SaveTo(file);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -40,5 +40,12 @@ namespace Ryujinx.HLE.HOS.Services.Nim
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(5)] // 17.0.0+
|
||||
// CreateServerInterface2(pid, handle<unknown>, u64) -> object<nn::ec::IshopServiceAccessServer>
|
||||
public ResultCode CreateServerInterface2(ServiceCtx context)
|
||||
{
|
||||
return CreateServerInterface(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,5 +9,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
||||
public byte Sm0TpcIndex;
|
||||
public byte Sm1GpcIndex;
|
||||
public byte Sm1TpcIndex;
|
||||
public uint Reserved;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||
using Ryujinx.HLE.HOS.Services.Apm;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -12,7 +13,7 @@ using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||
{
|
||||
class IUserInterface : IpcService
|
||||
partial class IUserInterface : IpcService
|
||||
{
|
||||
private static readonly Dictionary<string, Type> _services;
|
||||
|
||||
@ -95,9 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||
{
|
||||
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
|
||||
|
||||
IpcService service = serviceAttribute.Parameter != null
|
||||
? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
|
||||
: (IpcService)Activator.CreateInstance(type, context);
|
||||
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
|
||||
|
||||
service.TrySetServer(_commonServer);
|
||||
service.Server.AddSessionObj(session.ServerSession, service);
|
||||
|
@ -15,6 +15,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
||||
public KernelAccessControl KernelAccessControl { get; private set; }
|
||||
|
||||
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACI0 data.</exception>
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
|
||||
public Aci0(Stream stream, int offset)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -19,6 +19,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
||||
public KernelAccessControl KernelAccessControl { get; private set; }
|
||||
|
||||
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACID data.</exception>
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public Acid(Stream stream, int offset)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -11,6 +11,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
public int Unknown3 { get; private set; }
|
||||
public int Unknown4 { get; private set; }
|
||||
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public FsAccessControl(Stream stream, int offset, int size)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -9,6 +9,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
public int Version { get; private set; }
|
||||
public ulong PermissionsBitmask { get; private set; }
|
||||
|
||||
/// <exception cref="InvalidNpdmException">The stream contains invalid data.</exception>
|
||||
/// <exception cref="NotImplementedException">The ContentOwnerId section is not implemented.</exception>
|
||||
/// <exception cref="ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public FsAccessHeader(Stream stream, int offset, int size)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
public int[] Capabilities { get; private set; }
|
||||
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public KernelAccessControl(Stream stream, int offset, int size)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -24,6 +24,13 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
public Aci0 Aci0 { get; private set; }
|
||||
public Acid Acid { get; private set; }
|
||||
|
||||
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid NPDM data.</exception>
|
||||
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public Npdm(Stream stream)
|
||||
{
|
||||
BinaryReader reader = new(stream);
|
||||
|
@ -9,6 +9,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
public IReadOnlyDictionary<string, bool> Services { get; private set; }
|
||||
|
||||
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
|
||||
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
public ServiceAccessControl(Stream stream, int offset, int size)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
ulong titleIdBase = mainNca.GetProgramIdBase();
|
||||
|
||||
// Load update information if exists.
|
||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
|
||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
||||
if (File.Exists(titleUpdateMetadataPath))
|
||||
{
|
||||
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
|
||||
|
@ -118,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
device.Configuration.ContentManager.ClearAocData();
|
||||
|
||||
// Load DownloadableContents.
|
||||
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
||||
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
|
||||
if (File.Exists(addOnContentMetadataPath))
|
||||
{
|
||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -11,6 +12,7 @@
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="..\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\Ryujinx.Horizon\Ryujinx.Horizon.csproj" />
|
||||
@ -24,8 +26,8 @@
|
||||
<PackageReference Include="LibHac" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
|
||||
<PackageReference Include="MsgPack.Cli" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" />
|
||||
<PackageReference Include="SkiaSharp" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" />
|
||||
<PackageReference Include="NetCoreServer" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -21,6 +21,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
||||
|
||||
public long CurrentTime { get; private set; }
|
||||
|
||||
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
|
||||
|
||||
public MultiWaitImpl()
|
||||
{
|
||||
_multiWaits = new List<MultiWaitHolderBase>();
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Horizon.Sdk.OsTypes.Impl;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
@ -6,6 +7,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
private readonly MultiWaitImpl _impl;
|
||||
|
||||
public IEnumerable<MultiWaitHolderBase> MultiWaits => _impl.MultiWaits;
|
||||
|
||||
public MultiWait()
|
||||
{
|
||||
_impl = new MultiWaitImpl();
|
||||
|
@ -3,6 +3,7 @@ using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Cmif;
|
||||
using Ryujinx.Horizon.Sdk.Sm;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||
{
|
||||
@ -116,6 +117,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||
while (WaitAndProcessRequestsImpl())
|
||||
{
|
||||
}
|
||||
|
||||
// Unlink pending sessions, dispose expects them to be already unlinked.
|
||||
|
||||
ServerSession[] serverSessions = Enumerable.OfType<ServerSession>(_multiWait.MultiWaits).ToArray();
|
||||
|
||||
foreach (ServerSession serverSession in serverSessions)
|
||||
{
|
||||
if (serverSession.IsLinked)
|
||||
{
|
||||
serverSession.UnlinkFromMultiWaitHolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitAndProcessRequests()
|
||||
|
@ -4,6 +4,22 @@ namespace Ryujinx.Memory.Range
|
||||
{
|
||||
MultiRange Range { get; }
|
||||
|
||||
ulong BaseAddress => Range.GetSubRange(0).Address;
|
||||
ulong BaseAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int index = 0; index < Range.Count; index++)
|
||||
{
|
||||
MemoryRange subRange = Range.GetSubRange(index);
|
||||
|
||||
if (!MemoryRange.IsInvalid(ref subRange))
|
||||
{
|
||||
return subRange.Address;
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryRange.InvalidAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,11 @@ namespace Ryujinx.Memory.Range
|
||||
/// </summary>
|
||||
public readonly record struct MemoryRange
|
||||
{
|
||||
/// <summary>
|
||||
/// Special address value used to indicate than an address is invalid.
|
||||
/// </summary>
|
||||
internal const ulong InvalidAddress = ulong.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// An empty memory range, with a null address and zero size.
|
||||
/// </summary>
|
||||
@ -58,13 +63,24 @@ namespace Ryujinx.Memory.Range
|
||||
return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given sub-range of memory is invalid.
|
||||
/// Those are used to represent unmapped memory regions (holes in the region mapping).
|
||||
/// </summary>
|
||||
/// <param name="subRange">Memory range to check</param>
|
||||
/// <returns>True if the memory range is considered invalid, false otherwise</returns>
|
||||
internal static bool IsInvalid(ref MemoryRange subRange)
|
||||
{
|
||||
return subRange.Address == InvalidAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string summary of the memory range.
|
||||
/// </summary>
|
||||
/// <returns>A string summary of the memory range</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (Address == ulong.MaxValue)
|
||||
if (Address == InvalidAddress)
|
||||
{
|
||||
return $"[Unmapped 0x{Size:X}]";
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace Ryujinx.Memory.Range
|
||||
{
|
||||
var subrange = range.GetSubRange(i);
|
||||
|
||||
if (IsInvalid(ref subrange))
|
||||
if (MemoryRange.IsInvalid(ref subrange))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -56,7 +56,7 @@ namespace Ryujinx.Memory.Range
|
||||
{
|
||||
var subrange = range.GetSubRange(i);
|
||||
|
||||
if (IsInvalid(ref subrange))
|
||||
if (MemoryRange.IsInvalid(ref subrange))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -99,7 +99,7 @@ namespace Ryujinx.Memory.Range
|
||||
{
|
||||
var subrange = range.GetSubRange(i);
|
||||
|
||||
if (IsInvalid(ref subrange))
|
||||
if (MemoryRange.IsInvalid(ref subrange))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -142,17 +142,6 @@ namespace Ryujinx.Memory.Range
|
||||
return overlapCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given sub-range of memory is invalid.
|
||||
/// Those are used to represent unmapped memory regions (holes in the region mapping).
|
||||
/// </summary>
|
||||
/// <param name="subRange">Memory range to checl</param>
|
||||
/// <returns>True if the memory range is considered invalid, false otherwise</returns>
|
||||
private static bool IsInvalid(ref MemoryRange subRange)
|
||||
{
|
||||
return subRange.Address == ulong.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items on the list starting at the specified memory address.
|
||||
/// </summary>
|
||||
|
@ -130,9 +130,9 @@ namespace Ryujinx.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||
|
||||
Read(va, memoryOwner.Memory.Span);
|
||||
Read(va, memoryOwner.Span);
|
||||
|
||||
return new WritableRegion(this, va, memoryOwner);
|
||||
}
|
||||
|
@ -25,6 +25,24 @@ namespace Ryujinx.Tests.Cpu
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] UQAddSub16()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
0xe6600f10u, // UQADD16 R0, R0, R0
|
||||
0xe6600f70u, // UQSUB16 R0, R0, R0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] UQAddSub8()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
0xe6600f90u, // UQADD8 R0, R0, R0
|
||||
0xe6600ff0u, // UQSUB8 R0, R0, R0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] SsatUsat()
|
||||
{
|
||||
return new[]
|
||||
@ -182,6 +200,42 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void U_Q_AddSub_16([ValueSource(nameof(UQAddSub16))] uint opcode,
|
||||
[Values(0u, 0xdu)] uint rd,
|
||||
[Values(1u)] uint rm,
|
||||
[Values(2u)] uint rn,
|
||||
[Random(RndCnt)] uint w0,
|
||||
[Random(RndCnt)] uint w1,
|
||||
[Random(RndCnt)] uint w2)
|
||||
{
|
||||
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16);
|
||||
|
||||
uint sp = TestContext.CurrentContext.Random.NextUInt();
|
||||
|
||||
SingleOpcode(opcode, r0: w0, r1: w1, r2: w2, sp: sp);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void U_Q_AddSub_8([ValueSource(nameof(UQAddSub8))] uint opcode,
|
||||
[Values(0u, 0xdu)] uint rd,
|
||||
[Values(1u)] uint rm,
|
||||
[Values(2u)] uint rn,
|
||||
[Random(RndCnt)] uint w0,
|
||||
[Random(RndCnt)] uint w1,
|
||||
[Random(RndCnt)] uint w2)
|
||||
{
|
||||
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16);
|
||||
|
||||
uint sp = TestContext.CurrentContext.Random.NextUInt();
|
||||
|
||||
SingleOpcode(opcode, r0: w0, r1: w1, r2: w2, sp: sp);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Uadd8_Sel([Values(0u)] uint rd,
|
||||
[Values(1u)] uint rm,
|
||||
|
@ -327,6 +327,32 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VSWP D0, D0")]
|
||||
public void Vswp([Values(0u, 1u)] uint rd,
|
||||
[Values(0u, 1u)] uint rm,
|
||||
[Values] bool q)
|
||||
{
|
||||
uint opcode = 0xf3b20000u; // VSWP D0, D0
|
||||
|
||||
if (q)
|
||||
{
|
||||
opcode |= 1u << 6;
|
||||
|
||||
rd &= ~1u;
|
||||
rm &= ~1u;
|
||||
}
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
|
||||
V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -909,6 +909,39 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VQRDMULH.<S16, S32> <Qd>, <Qn>, <Qm>")]
|
||||
public void Vqrdmulh_I([Range(0u, 5u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[ValueSource(nameof(_8B4H2S1D_))] ulong z,
|
||||
[ValueSource(nameof(_8B4H2S1D_))] ulong a,
|
||||
[ValueSource(nameof(_8B4H2S1D_))] ulong b,
|
||||
[Values(1u, 2u)] uint size) // <S16, S32>
|
||||
{
|
||||
rd >>= 1;
|
||||
rd <<= 1;
|
||||
rn >>= 1;
|
||||
rn <<= 1;
|
||||
rm >>= 1;
|
||||
rm <<= 1;
|
||||
|
||||
uint opcode = 0xf3100b40u & ~(3u << 20); // VQRDMULH.S16 Q0, Q0, Q0
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
opcode |= (size & 0x3) << 20;
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, ~z);
|
||||
V128 v1 = MakeVectorE0E1(a, ~a);
|
||||
V128 v2 = MakeVectorE0E1(b, ~b);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vp_Add_Long_Accumulate([Values(0u, 2u, 4u, 8u)] uint rd,
|
||||
[Values(0u, 2u, 4u, 8u)] uint rm,
|
||||
|
@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VSHL.<size> {<Vd>}, <Vm>, #<imm>")]
|
||||
public void Vshl_Imm([Values(0u)] uint rd,
|
||||
public void Vshl_Imm([Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u, 3u)] uint size,
|
||||
[Random(RndCntShiftImm)] uint shiftImm,
|
||||
@ -262,6 +262,40 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VSLI.<size> {<Vd>}, <Vm>, #<imm>")]
|
||||
public void Vsli([Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u, 3u)] uint size,
|
||||
[Random(RndCntShiftImm)] uint shiftImm,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool q)
|
||||
{
|
||||
uint opcode = 0xf3800510u; // VORR.I32 D0, #0x800000 (immediate value changes it into SLI)
|
||||
if (q)
|
||||
{
|
||||
opcode |= 1 << 6;
|
||||
rm <<= 1;
|
||||
rd <<= 1;
|
||||
}
|
||||
|
||||
uint imm = 1u << ((int)size + 3);
|
||||
imm |= shiftImm & (imm - 1);
|
||||
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((imm & 0x3f) << 16) | ((imm & 0x40) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, z);
|
||||
V128 v1 = MakeVectorE0E1(a, z);
|
||||
V128 v2 = MakeVectorE0E1(b, z);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vqshrn_Vqrshrn_Vrshrn_Imm([ValueSource(nameof(_Vqshrn_Vqrshrn_Vrshrn_Imm_))] uint opcode,
|
||||
[Values(0u, 1u)] uint rd,
|
||||
|
@ -42,6 +42,8 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
[JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
|
||||
|
||||
[JsonIgnore] public string IdBaseString => IdBase.ToString("x16");
|
||||
|
||||
public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
|
||||
{
|
||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||
|
@ -34,6 +34,7 @@ namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
public class ApplicationLibrary
|
||||
{
|
||||
public Language DesiredLanguage { get; set; }
|
||||
public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
|
||||
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||
|
||||
@ -45,7 +46,6 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly IntegrityCheckLevel _checkLevel;
|
||||
private Language _desiredTitleLanguage;
|
||||
private CancellationTokenSource _cancellationToken;
|
||||
|
||||
private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
@ -72,37 +72,43 @@ namespace Ryujinx.UI.App.Common
|
||||
return resourceByteArray;
|
||||
}
|
||||
|
||||
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
|
||||
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
|
||||
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath)
|
||||
{
|
||||
ApplicationData data = new()
|
||||
{
|
||||
Icon = _nspIcon,
|
||||
Path = filePath,
|
||||
};
|
||||
|
||||
using UniqueRef<IFile> npdmFile = new();
|
||||
|
||||
try
|
||||
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||
|
||||
if (ResultFs.PathNotFound.Includes(result))
|
||||
{
|
||||
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||
Npdm npdm = new(npdmFile.Get.AsStream());
|
||||
|
||||
if (ResultFs.PathNotFound.Includes(result))
|
||||
{
|
||||
Npdm npdm = new(npdmFile.Get.AsStream());
|
||||
|
||||
data.Name = npdm.TitleName;
|
||||
data.Id = npdm.Aci0.TitleId;
|
||||
}
|
||||
|
||||
return data;
|
||||
data.Name = npdm.TitleName;
|
||||
data.Id = npdm.Aci0.TitleId;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
|
||||
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
|
||||
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
|
||||
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
|
||||
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
|
||||
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
|
||||
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
|
||||
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
|
||||
/// <exception cref="IOException">An I/O error occurred.</exception>
|
||||
private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath)
|
||||
{
|
||||
bool isExeFs = false;
|
||||
@ -170,99 +176,88 @@ namespace Ryujinx.UI.App.Common
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
|
||||
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
|
||||
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
|
||||
private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath)
|
||||
{
|
||||
var applications = new List<ApplicationData>();
|
||||
string extension = Path.GetExtension(filePath).ToLower();
|
||||
|
||||
try
|
||||
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
|
||||
{
|
||||
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
ApplicationData applicationData = new()
|
||||
Id = titleId,
|
||||
Path = filePath,
|
||||
};
|
||||
|
||||
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
||||
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
||||
|
||||
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
||||
|
||||
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
|
||||
|
||||
// Check if there is an update available.
|
||||
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
|
||||
{
|
||||
// Replace the original ControlFs by the updated one.
|
||||
controlFs = updatedControlFs;
|
||||
}
|
||||
|
||||
if (controlFs == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadControlData(controlFs, controlHolder.ByteSpan);
|
||||
|
||||
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
|
||||
|
||||
// Read the icon from the ControlFS and store it as a byte array
|
||||
try
|
||||
{
|
||||
using UniqueRef<IFile> icon = new();
|
||||
|
||||
controlFs.OpenFile(ref icon.Ref, $"/icon_{DesiredLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
using MemoryStream stream = new();
|
||||
|
||||
icon.Get.AsStream().CopyTo(stream);
|
||||
applicationData.Icon = stream.ToArray();
|
||||
}
|
||||
catch (HorizonResultException)
|
||||
{
|
||||
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
|
||||
{
|
||||
Id = titleId,
|
||||
Path = filePath,
|
||||
};
|
||||
if (entry.Name == "control.nacp")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
||||
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
||||
using var icon = new UniqueRef<IFile>();
|
||||
|
||||
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
||||
|
||||
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
|
||||
|
||||
// Check if there is an update available.
|
||||
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
|
||||
{
|
||||
// Replace the original ControlFs by the updated one.
|
||||
controlFs = updatedControlFs;
|
||||
}
|
||||
|
||||
if (controlFs == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadControlData(controlFs, controlHolder.ByteSpan);
|
||||
|
||||
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
|
||||
|
||||
// Read the icon from the ControlFS and store it as a byte array
|
||||
try
|
||||
{
|
||||
using UniqueRef<IFile> icon = new();
|
||||
|
||||
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
using MemoryStream stream = new();
|
||||
|
||||
icon.Get.AsStream().CopyTo(stream);
|
||||
applicationData.Icon = stream.ToArray();
|
||||
}
|
||||
catch (HorizonResultException)
|
||||
{
|
||||
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
|
||||
|
||||
if (applicationData.Icon != null)
|
||||
{
|
||||
if (entry.Name == "control.nacp")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using var icon = new UniqueRef<IFile>();
|
||||
|
||||
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
using MemoryStream stream = new();
|
||||
|
||||
icon.Get.AsStream().CopyTo(stream);
|
||||
applicationData.Icon = stream.ToArray();
|
||||
|
||||
if (applicationData.Icon != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
||||
}
|
||||
|
||||
applicationData.ControlHolder = controlHolder;
|
||||
|
||||
applications.Add(applicationData);
|
||||
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
||||
}
|
||||
}
|
||||
catch (MissingKeyException exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}");
|
||||
|
||||
applicationData.ControlHolder = controlHolder;
|
||||
|
||||
applications.Add(applicationData);
|
||||
}
|
||||
|
||||
return applications;
|
||||
@ -271,8 +266,18 @@ namespace Ryujinx.UI.App.Common
|
||||
public bool TryGetApplicationsFromFile(string applicationPath, out List<ApplicationData> applications)
|
||||
{
|
||||
applications = [];
|
||||
long fileSize;
|
||||
|
||||
long fileSize = new FileInfo(applicationPath).Length;
|
||||
try
|
||||
{
|
||||
fileSize = new FileInfo(applicationPath).Length;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file was not found: '{applicationPath}'");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
||||
|
||||
@ -319,52 +324,43 @@ namespace Ryujinx.UI.App.Common
|
||||
BinaryReader reader = new(file);
|
||||
ApplicationData application = new();
|
||||
|
||||
try
|
||||
file.Seek(24, SeekOrigin.Begin);
|
||||
|
||||
int assetOffset = reader.ReadInt32();
|
||||
|
||||
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
|
||||
{
|
||||
file.Seek(24, SeekOrigin.Begin);
|
||||
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
||||
|
||||
int assetOffset = reader.ReadInt32();
|
||||
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
||||
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
||||
|
||||
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
|
||||
ulong nacpOffset = reader.ReadUInt64();
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
// Reads and stores game icon as byte array
|
||||
if (iconSize > 0)
|
||||
{
|
||||
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
||||
|
||||
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
||||
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
||||
|
||||
ulong nacpOffset = reader.ReadUInt64();
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
// Reads and stores game icon as byte array
|
||||
if (iconSize > 0)
|
||||
{
|
||||
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
application.Icon = _nroIcon;
|
||||
}
|
||||
|
||||
// Read the NACP data
|
||||
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
||||
|
||||
GetApplicationInformation(ref controlHolder.Value, ref application);
|
||||
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
application.Icon = _nroIcon;
|
||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||
}
|
||||
|
||||
application.ControlHolder = controlHolder;
|
||||
applications.Add(application);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
|
||||
// Read the NACP data
|
||||
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
||||
|
||||
return false;
|
||||
GetApplicationInformation(ref controlHolder.Value, ref application);
|
||||
}
|
||||
else
|
||||
{
|
||||
application.Icon = _nroIcon;
|
||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||
}
|
||||
|
||||
application.ControlHolder = controlHolder;
|
||||
applications.Add(application);
|
||||
|
||||
break;
|
||||
|
||||
@ -377,34 +373,21 @@ namespace Ryujinx.UI.App.Common
|
||||
}
|
||||
case ".nca":
|
||||
{
|
||||
try
|
||||
ApplicationData application = new();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
|
||||
|
||||
if (!nca.IsProgram() || nca.IsPatch())
|
||||
{
|
||||
ApplicationData application = new();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
|
||||
|
||||
if (!nca.IsProgram() || nca.IsPatch())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
application.Icon = _ncaIcon;
|
||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||
application.ControlHolder = controlHolder;
|
||||
|
||||
applications.Add(application);
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
application.Icon = _ncaIcon;
|
||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||
application.ControlHolder = controlHolder;
|
||||
|
||||
applications.Add(application);
|
||||
|
||||
break;
|
||||
}
|
||||
// If its an NSO we just set defaults
|
||||
@ -417,48 +400,72 @@ namespace Ryujinx.UI.App.Common
|
||||
};
|
||||
|
||||
applications.Add(application);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MissingKeyException exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, exception.Message);
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var data in applications)
|
||||
{
|
||||
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
|
||||
// Only load metadata for applications with an ID
|
||||
if (data.Id != 0)
|
||||
{
|
||||
appMetadata.Title = data.Name;
|
||||
|
||||
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
|
||||
if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
|
||||
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
|
||||
{
|
||||
appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
|
||||
appMetadata.TimePlayedOld = default;
|
||||
}
|
||||
appMetadata.Title = data.Name;
|
||||
|
||||
// Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
|
||||
if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
|
||||
{
|
||||
// Migrate from string-based last_played to DateTime-based last_played_utc.
|
||||
if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
|
||||
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
|
||||
if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
|
||||
{
|
||||
appMetadata.LastPlayed = lastPlayedOldParsed;
|
||||
|
||||
// Migration successful: deleting last_played from the metadata file.
|
||||
appMetadata.LastPlayedOld = default;
|
||||
appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
|
||||
appMetadata.TimePlayedOld = default;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
// Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
|
||||
if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
|
||||
{
|
||||
// Migrate from string-based last_played to DateTime-based last_played_utc.
|
||||
if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
|
||||
{
|
||||
appMetadata.LastPlayed = lastPlayedOldParsed;
|
||||
|
||||
// Migration successful: deleting last_played from the metadata file.
|
||||
appMetadata.LastPlayedOld = default;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
data.Favorite = appMetadata.Favorite;
|
||||
data.TimePlayed = appMetadata.TimePlayed;
|
||||
data.LastPlayed = appMetadata.LastPlayed;
|
||||
}
|
||||
|
||||
data.Favorite = appMetadata.Favorite;
|
||||
data.TimePlayed = appMetadata.TimePlayed;
|
||||
data.LastPlayed = appMetadata.LastPlayed;
|
||||
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
||||
data.FileSize = fileSize;
|
||||
data.Path = applicationPath;
|
||||
@ -480,13 +487,11 @@ namespace Ryujinx.UI.App.Common
|
||||
controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
|
||||
}
|
||||
|
||||
public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage)
|
||||
public void LoadApplications(List<string> appDirs)
|
||||
{
|
||||
int numApplicationsFound = 0;
|
||||
int numApplicationsLoaded = 0;
|
||||
|
||||
_desiredTitleLanguage = desiredTitleLanguage;
|
||||
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
|
||||
// Builds the applications list with paths to found applications
|
||||
@ -510,7 +515,13 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file =>
|
||||
EnumerationOptions options = new()
|
||||
{
|
||||
RecurseSubdirectories = true,
|
||||
IgnoreInaccessible = false,
|
||||
};
|
||||
|
||||
IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options).Where(file =>
|
||||
{
|
||||
return
|
||||
(Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) ||
|
||||
@ -529,14 +540,18 @@ namespace Ryujinx.UI.App.Common
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(app);
|
||||
string extension = fileInfo.Extension.ToLower();
|
||||
|
||||
if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso")
|
||||
try
|
||||
{
|
||||
var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName;
|
||||
|
||||
applicationPaths.Add(fullPath);
|
||||
numApplicationsFound++;
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Failed to resolve the full path to file: \"{app}\" Error: {exception}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
@ -835,7 +850,7 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
private void GetApplicationInformation(ref ApplicationControlProperty controlData, ref ApplicationData data)
|
||||
{
|
||||
_ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
||||
_ = Enum.TryParse(DesiredLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
||||
|
||||
if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
|
||||
{
|
||||
|
@ -1,10 +1,7 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using ShellLink;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -21,8 +18,8 @@ namespace Ryujinx.UI.Common.Helper
|
||||
iconPath += ".ico";
|
||||
|
||||
MemoryStream iconDataStream = new(iconData);
|
||||
var image = Image.Load(iconDataStream);
|
||||
image.Mutate(x => x.Resize(128, 128));
|
||||
using var image = SKBitmap.Decode(iconDataStream);
|
||||
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
|
||||
SaveBitmapAsIcon(image, iconPath);
|
||||
|
||||
var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
|
||||
@ -37,8 +34,10 @@ namespace Ryujinx.UI.Common.Helper
|
||||
var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop");
|
||||
iconPath += ".png";
|
||||
|
||||
var image = Image.Load<Rgba32>(iconData);
|
||||
image.SaveAsPng(iconPath);
|
||||
var image = SKBitmap.Decode(iconData);
|
||||
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
|
||||
using var file = File.OpenWrite(iconPath);
|
||||
data.SaveTo(file);
|
||||
|
||||
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
|
||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
|
||||
@ -78,8 +77,10 @@ namespace Ryujinx.UI.Common.Helper
|
||||
}
|
||||
|
||||
const string IconName = "icon.png";
|
||||
var image = Image.Load<Rgba32>(iconData);
|
||||
image.SaveAsPng(Path.Combine(resourceFolderPath, IconName));
|
||||
var image = SKBitmap.Decode(iconData);
|
||||
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
|
||||
using var file = File.OpenWrite(Path.Combine(resourceFolderPath, IconName));
|
||||
data.SaveTo(file);
|
||||
|
||||
// plist file
|
||||
using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist"));
|
||||
@ -148,7 +149,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
/// <param name="source">The source bitmap image that will be saved as an .ico file</param>
|
||||
/// <param name="filePath">The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).</param>
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static void SaveBitmapAsIcon(Image source, string filePath)
|
||||
private static void SaveBitmapAsIcon(SKBitmap source, string filePath)
|
||||
{
|
||||
// Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz
|
||||
byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 };
|
||||
@ -156,13 +157,16 @@ namespace Ryujinx.UI.Common.Helper
|
||||
|
||||
fs.Write(header);
|
||||
// Writing actual data
|
||||
source.Save(fs, PngFormat.Instance);
|
||||
using var data = source.Encode(SKEncodedImageFormat.Png, 100);
|
||||
data.SaveTo(fs);
|
||||
// Getting data length (file length minus header)
|
||||
long dataLength = fs.Length - header.Length;
|
||||
// Write it in the correct place
|
||||
fs.Seek(14, SeekOrigin.Begin);
|
||||
fs.WriteByte((byte)dataLength);
|
||||
fs.WriteByte((byte)(dataLength >> 8));
|
||||
fs.WriteByte((byte)(dataLength >> 16));
|
||||
fs.WriteByte((byte)(dataLength >> 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,32 +367,24 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
|
||||
using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
|
||||
|
||||
Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length);
|
||||
|
||||
SKBitmap bitmapToSave = null;
|
||||
using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
|
||||
using SKCanvas canvas = new SKCanvas(bitmapToSave);
|
||||
|
||||
if (e.FlipX || e.FlipY)
|
||||
{
|
||||
bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
|
||||
canvas.Clear(SKColors.Black);
|
||||
|
||||
using var canvas = new SKCanvas(bitmapToSave);
|
||||
float scaleX = e.FlipX ? -1 : 1;
|
||||
float scaleY = e.FlipY ? -1 : 1;
|
||||
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
|
||||
|
||||
float scaleX = e.FlipX ? -1 : 1;
|
||||
float scaleY = e.FlipY ? -1 : 1;
|
||||
canvas.SetMatrix(matrix);
|
||||
canvas.DrawBitmap(bitmap, SKPoint.Empty);
|
||||
|
||||
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
|
||||
|
||||
canvas.SetMatrix(matrix);
|
||||
|
||||
canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0));
|
||||
}
|
||||
|
||||
SaveBitmapAsPng(bitmapToSave ?? bitmap, path);
|
||||
bitmapToSave?.Dispose();
|
||||
SaveBitmapAsPng(bitmapToSave, path);
|
||||
|
||||
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
var builder = new CompiledBindingPathBuilder();
|
||||
|
||||
builder.SetRawSource(LocaleManager.Instance)
|
||||
builder
|
||||
.Property(new ClrPropertyInfo("Item",
|
||||
obj => (LocaleManager.Instance[keyToUse]),
|
||||
null,
|
||||
@ -32,7 +32,10 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
var path = builder.Build();
|
||||
|
||||
var binding = new CompiledBindingExtension(path);
|
||||
var binding = new CompiledBindingExtension(path)
|
||||
{
|
||||
Source = LocaleManager.Instance
|
||||
};
|
||||
|
||||
return binding.ProvideValue(serviceProvider);
|
||||
}
|
||||
|
@ -139,9 +139,11 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
foreach (var item in locale)
|
||||
{
|
||||
this[item.Key] = item.Value;
|
||||
_localeStrings[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
OnPropertyChanged("Item");
|
||||
|
||||
LocaleChanged?.Invoke();
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user