diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index ffb5d5f8b..68be1f5e0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -23,7 +23,7 @@ body: attributes: label: Log file description: A log file will help our developers to better diagnose and fix the issue. - placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste). + placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area validations: required: true - type: input @@ -83,4 +83,4 @@ body: - Additional info about your environment: - Any other information relevant to your issue. validations: - required: false + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 399aa039c..383bbb151 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,6 @@ name: Feature Request description: Suggest a new feature for Ryujinx. title: "[Feature Request]" -labels: enhancement body: - type: textarea id: overview diff --git a/Directory.Packages.props b/Directory.Packages.props index 301024cf8..d04e237e0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,18 +11,18 @@ - + - + - + - + @@ -39,14 +39,14 @@ - - - - - + + + + + - \ No newline at end of file + diff --git a/Ryujinx.sln b/Ryujinx.sln index 76ebd573f..b8304164d 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -87,8 +87,6 @@ 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 @@ -251,10 +249,6 @@ 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 diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs index 89b1e9e6b..12ebabddd 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64 long originalPosition = _stream.Position; _stream.Seek(0, SeekOrigin.Begin); - _stream.ReadExactly(code, 0, code.Length); + _stream.Read(code, 0, code.Length); _stream.Seek(originalPosition, SeekOrigin.Begin); RelocInfo relocInfo; diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index 16feeb914..f156e0886 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -251,20 +251,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - // If this is a copy destination variable, we prefer the register used for the copy source. - // If the register is available, then the copy can be eliminated later as both source - // and destination will use the same register. - int selectedReg; - - if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd()) - { - selectedReg = preferredReg; - } - else - { - selectedReg = GetHighestValueIndex(freePositions); - } - + int selectedReg = GetHighestValueIndex(freePositions); int selectedNextUse = freePositions[selectedReg]; // Intervals starts and ends at odd positions, unless they span an entire @@ -444,7 +431,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - private static int GetHighestValueIndex(ReadOnlySpan span) + private static int GetHighestValueIndex(Span span) { int highest = int.MinValue; @@ -811,12 +798,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // The "visited" state is stored in the MSB of the local's value. const ulong VisitedMask = 1ul << 63; - static bool IsVisited(Operand local) + bool IsVisited(Operand local) { return (local.GetValueUnsafe() & VisitedMask) != 0; } - static void SetVisited(Operand local) + void SetVisited(Operand local) { local.GetValueUnsafe() |= VisitedMask; } @@ -839,25 +826,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { dest.NumberLocal(_intervals.Count); - LiveInterval interval = new LiveInterval(dest); - _intervals.Add(interval); + _intervals.Add(new LiveInterval(dest)); SetVisited(dest); - - // If this is a copy (or copy-like operation), set the copy source interval as well. - // This is used for register preferencing later on, which allows the copy to be eliminated - // in some cases. - if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32) - { - Operand source = node.GetSource(0); - - if (source.Kind == OperandKind.LocalVariable && - source.GetLocalNumber() > 0 && - (node.Instruction == Instruction.Copy || source.Type == OperandType.I32)) - { - interval.SetCopySource(_intervals[source.GetLocalNumber()]); - } - } } } } diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs index cfe1bc7ca..333d3951b 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs @@ -19,7 +19,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public LiveRange CurrRange; public LiveInterval Parent; - public LiveInterval CopySource; public UseList Uses; public LiveIntervalList Children; @@ -38,7 +37,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private ref LiveRange CurrRange => ref _data->CurrRange; private ref LiveRange PrevRange => ref _data->PrevRange; private ref LiveInterval Parent => ref _data->Parent; - private ref LiveInterval CopySource => ref _data->CopySource; private ref UseList Uses => ref _data->Uses; private ref LiveIntervalList Children => ref _data->Children; @@ -80,25 +78,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators Register = register; } - public void SetCopySource(LiveInterval copySource) - { - CopySource = copySource; - } - - public bool TryGetCopySourceRegister(out int copySourceRegIndex) - { - if (CopySource._data != null) - { - copySourceRegIndex = CopySource.Register.Index; - - return true; - } - - copySourceRegIndex = 0; - - return false; - } - public void Reset() { PrevRange = default; diff --git a/src/ARMeilleure/CodeGen/X86/Assembler.cs b/src/ARMeilleure/CodeGen/X86/Assembler.cs index 96f4de049..55bf07248 100644 --- a/src/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/src/ARMeilleure/CodeGen/X86/Assembler.cs @@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86 Span buffer = new byte[jump.JumpPosition - _stream.Position]; - _stream.ReadExactly(buffer); + _stream.Read(buffer); _stream.Seek(ReservedBytesForJump, SeekOrigin.Current); codeStream.Write(buffer); diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs index 20d567fe5..edc004125 100644 --- a/src/ARMeilleure/Decoders/OpCodeTable.cs +++ b/src/ARMeilleure/Decoders/OpCodeTable.cs @@ -746,7 +746,6 @@ namespace ARMeilleure.Decoders SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create); SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create); SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create); - SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create); SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create); SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create); SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create); @@ -823,10 +822,6 @@ 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); @@ -1012,8 +1007,6 @@ 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); @@ -1035,10 +1028,8 @@ namespace ARMeilleure.Decoders SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding. - SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 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); @@ -1063,7 +1054,6 @@ namespace ARMeilleure.Decoders SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); SetAsimd("1111001x1x< - { - EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false); - })); - } - public static void Rbit(ArmEmitterContext context) { Operand m = GetAluM(context); @@ -570,46 +558,6 @@ 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; @@ -986,251 +934,6 @@ namespace ARMeilleure.Instructions } } - private static void EmitSaturateRange(ArmEmitterContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true) - { - Debug.Assert(saturateTo <= 32); - Debug.Assert(!unsigned || saturateTo < 32); - - if (!unsigned && 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; - } - - Operand satValue; - - if (unsigned) - { - // Negative values always saturate (to zero). - // So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one. - - satValue = context.BitwiseAnd(value, Const((int)(uint.MaxValue >> (32 - (int)saturateTo)))); - } - else - { - satValue = context.ShiftLeft(value, Const(32 - (int)saturateTo)); - satValue = context.ShiftRightSI(satValue, Const(32 - (int)saturateTo)); - } - - // If the result is 0, the values are equal and we don't need saturation. - Operand lblNoSat = Label(); - context.BranchIfFalse(lblNoSat, context.Subtract(value, satValue)); - - // Saturate and set Q flag. - if (unsigned) - { - if (saturateTo == 31) - { - // Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned - // is when the signed input is negative, as all positive values are representable on a 31 bits range. - - satValue = Const(0); - } - else - { - satValue = context.ShiftRightSI(value, Const(31)); - satValue = context.BitwiseNot(satValue); - satValue = context.ShiftRightUI(satValue, Const(32 - (int)saturateTo)); - } - } - else - { - if (saturateTo == 1) - { - satValue = context.ShiftRightSI(value, Const(31)); - } - else - { - satValue = Const(uint.MaxValue >> (33 - (int)saturateTo)); - satValue = context.BitwiseExclusiveOr(satValue, context.ShiftRightSI(value, Const(31))); - } - } - - if (setQ) - { - SetFlag(context, PState.QFlag, Const(1)); - } - - context.Copy(result, satValue); - - Operand lblExit = Label(); - context.Branch(lblExit); - - context.MarkLabel(lblNoSat); - - context.Copy(result, value); - - context.MarkLabel(lblExit); - } - - 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 EmitSigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction) - { - Operand tempD = context.AllocateLocal(OperandType.I32); - - Operand tempN = context.SignExtend16(OperandType.I32, rn); - Operand tempM = context.SignExtend16(OperandType.I32, rm); - elementAction(tempD, tempN, tempM); - Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD); - - tempN = context.ShiftRightSI(rn, Const(16)); - tempM = context.ShiftRightSI(rm, Const(16)); - elementAction(tempD, tempN, tempM); - return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16))); - } - - private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action 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 elementAction) - { - return Emit8BitPair(context, rn, rm, elementAction, unsigned: false); - } - - private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction) - { - return Emit8BitPair(context, rn, rm, elementAction, unsigned: true); - } - - private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action 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; diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index c807fc858..dc2646a55 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -1246,33 +1246,6 @@ 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; diff --git a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs index fb2641f66..9fa740997 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -191,26 +191,6 @@ 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; diff --git a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs index eb28a0c5a..e40600a47 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs @@ -106,38 +106,6 @@ namespace ARMeilleure.Instructions context.Copy(GetVecA32(op.Qd), res); } - public static void Vshll2(ArmEmitterContext context) - { - OpCode32Simd op = (OpCode32Simd)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U); - - if (op.Size == 2) - { - if (op.U) - { - me = context.ZeroExtend32(OperandType.I64, me); - } - else - { - me = context.SignExtend32(OperandType.I64, me); - } - } - - me = context.ShiftLeft(me, Const(8 << op.Size)); - - res = EmitVectorInsert(context, res, me, index, op.Size + 1); - } - - context.Copy(GetVecA32(op.Qd), res); - } - public static void Vshr(ArmEmitterContext context) { OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp; @@ -162,36 +130,6 @@ 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; diff --git a/src/ARMeilleure/Instructions/InstName.cs b/src/ARMeilleure/Instructions/InstName.cs index 74c33155b..457abbf49 100644 --- a/src/ARMeilleure/Instructions/InstName.cs +++ b/src/ARMeilleure/Instructions/InstName.cs @@ -527,7 +527,6 @@ namespace ARMeilleure.Instructions Pld, Pop, Push, - Qadd16, Rev, Revsh, Rsb, @@ -572,10 +571,6 @@ namespace ARMeilleure.Instructions Umaal, Umlal, Umull, - Uqadd16, - Uqadd8, - Uqsub16, - Uqsub8, Usat, Usat16, Usub8, @@ -650,7 +645,6 @@ namespace ARMeilleure.Instructions Vqdmulh, Vqmovn, Vqmovun, - Vqrdmulh, Vqrshrn, Vqrshrun, Vqshrn, @@ -672,7 +666,6 @@ namespace ARMeilleure.Instructions Vshll, Vshr, Vshrn, - Vsli, Vst1, Vst2, Vst3, @@ -689,7 +682,6 @@ namespace ARMeilleure.Instructions Vsub, Vsubl, Vsubw, - Vswp, Vtbl, Vtrn, Vtst, diff --git a/src/ARMeilleure/Translation/ControlFlowGraph.cs b/src/ARMeilleure/Translation/ControlFlowGraph.cs index 45b092ec5..3ead49c93 100644 --- a/src/ARMeilleure/Translation/ControlFlowGraph.cs +++ b/src/ARMeilleure/Translation/ControlFlowGraph.cs @@ -11,7 +11,7 @@ namespace ARMeilleure.Translation private int[] _postOrderMap; public int LocalsCount { get; private set; } - public BasicBlock Entry { get; private set; } + public BasicBlock Entry { get; } public IntrusiveList Blocks { get; } public BasicBlock[] PostOrderBlocks => _postOrderBlocks; public int[] PostOrderMap => _postOrderMap; @@ -34,15 +34,6 @@ namespace ARMeilleure.Translation return result; } - public void UpdateEntry(BasicBlock newEntry) - { - newEntry.AddSuccessor(Entry); - - Entry = newEntry; - Blocks.AddFirst(newEntry); - Update(); - } - public void Update() { RemoveUnreachableBlocks(Blocks); diff --git a/src/ARMeilleure/Translation/DelegateInfo.cs b/src/ARMeilleure/Translation/DelegateInfo.cs index 706625437..27479a003 100644 --- a/src/ARMeilleure/Translation/DelegateInfo.cs +++ b/src/ARMeilleure/Translation/DelegateInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace ARMeilleure.Translation { @@ -10,10 +11,11 @@ namespace ARMeilleure.Translation public IntPtr FuncPtr { get; } - public DelegateInfo(Delegate dlg, IntPtr funcPtr) + public DelegateInfo(Delegate dlg) { _dlg = dlg; - FuncPtr = funcPtr; + + FuncPtr = Marshal.GetFunctionPointerForDelegate(dlg); } } } diff --git a/src/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs index 66412b8e6..63db789df 100644 --- a/src/ARMeilleure/Translation/Delegates.cs +++ b/src/ARMeilleure/Translation/Delegates.cs @@ -3,7 +3,6 @@ using ARMeilleure.State; using System; using System.Collections.Generic; using System.Reflection; -using System.Runtime.InteropServices; namespace ARMeilleure.Translation { @@ -65,11 +64,11 @@ namespace ARMeilleure.Translation return index; } - private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr) + private static void SetDelegateInfo(Delegate dlg) { string key = GetKey(dlg.Method); - _delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key). + _delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key). } private static string GetKey(MethodInfo info) @@ -83,353 +82,179 @@ namespace ARMeilleure.Translation { _delegates = new SortedList(); - var dlgMathAbs = new MathAbs(Math.Abs); - var dlgMathCeiling = new MathCeiling(Math.Ceiling); - var dlgMathFloor = new MathFloor(Math.Floor); - var dlgMathRound = new MathRound(Math.Round); - var dlgMathTruncate = new MathTruncate(Math.Truncate); + SetDelegateInfo(new MathAbs(Math.Abs)); + SetDelegateInfo(new MathCeiling(Math.Ceiling)); + SetDelegateInfo(new MathFloor(Math.Floor)); + SetDelegateInfo(new MathRound(Math.Round)); + SetDelegateInfo(new MathTruncate(Math.Truncate)); - var dlgMathFAbs = new MathFAbs(MathF.Abs); - var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling); - var dlgMathFFloor = new MathFFloor(MathF.Floor); - var dlgMathFRound = new MathFRound(MathF.Round); - var dlgMathFTruncate = new MathFTruncate(MathF.Truncate); + SetDelegateInfo(new MathFAbs(MathF.Abs)); + SetDelegateInfo(new MathFCeiling(MathF.Ceiling)); + SetDelegateInfo(new MathFFloor(MathF.Floor)); + SetDelegateInfo(new MathFRound(MathF.Round)); + SetDelegateInfo(new MathFTruncate(MathF.Truncate)); - var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break); - var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization); - var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit); - var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0); - var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0); - var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0); - var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0); - var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0); - var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress); - var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine); - var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte); - var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16); - var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32); - var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64); - var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128); - var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking); - var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall); - var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess); - var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined); - var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte); - var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16); - var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32); - var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64); - var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128); + SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break)); + SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization)); + SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit)); + SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0)); + SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0)); + SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0)); + SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0)); + SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0)); + SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress)); + SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine)); + SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte)); + SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16)); + SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32)); + SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64)); + SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128)); + SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking)); + SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall)); + SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess)); + SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined)); + SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte)); + SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16)); + SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32)); + SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64)); + SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128)); - var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns); - var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros); - var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b); - var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb); - var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch); - var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw); - var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx); - var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h); - var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w); - var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x); - var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt); - var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt); - var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate); - var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose); - var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower); - var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority); - var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity); - var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper); - var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns); - var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns); - var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128); - var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32); - var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64); - var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32); - var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64); - var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32); - var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64); - var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32); - var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64); - var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1); - var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2); - var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1); - var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2); - var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64); - var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1); - var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2); - var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3); - var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4); - var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1); - var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2); - var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3); - var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4); - var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64); + SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns)); + SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros)); + SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b)); + SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb)); + SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch)); + SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw)); + SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx)); + SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h)); + SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w)); + SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x)); + SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt)); + SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt)); + SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate)); + SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose)); + SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower)); + SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority)); + SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity)); + SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper)); + SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns)); + SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns)); + SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128)); + SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32)); + SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64)); + SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32)); + SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64)); + SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32)); + SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64)); + SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32)); + SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64)); + SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1)); + SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2)); + SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1)); + SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2)); + SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64)); + SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1)); + SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2)); + SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3)); + SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4)); + SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1)); + SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2)); + SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3)); + SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4)); + SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64)); - var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert); - var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert); + SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert)); + SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert)); - var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd); - var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only. - var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare); - var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ); - var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only. - var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE); - var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only. - var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT); - var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only. - var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE); - var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only. - var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT); - var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only. - var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv); - var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax); - var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only. - var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum); - var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only. - var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin); - var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only. - var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum); - var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only. - var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul); - var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only. - var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd); - var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only. - var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub); - var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only. - var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX); - var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd); - var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub); - var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate); - var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only. - var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only. - var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused); - var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX); - var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate); - var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only. - var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only. - var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused); - var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt); - var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub); + SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd)); + SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare)); + SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ)); + SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE)); + SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT)); + SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE)); + SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT)); + SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv)); + SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax)); + SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum)); + SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin)); + SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum)); + SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul)); + SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd)); + SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub)); + SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX)); + SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd)); + SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub)); + SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate)); + SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused)); + SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX)); + SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate)); + SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused)); + SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt)); + SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub)); - var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert); + SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert)); - var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd); - var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only. - var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare); - var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ); - var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only. - var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE); - var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only. - var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT); - var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only. - var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE); - var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only. - var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT); - var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only. - var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv); - var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax); - var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only. - var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum); - var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only. - var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin); - var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only. - var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum); - var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only. - var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul); - var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only. - var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd); - var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only. - var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub); - var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only. - var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX); - var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd); - var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub); - var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate); - var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only. - var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only. - var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused); - var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX); - var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate); - var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only. - var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only. - var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused); - var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt); - var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub); + SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd)); + SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare)); + SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ)); + SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE)); + SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT)); + SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE)); + SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT)); + SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv)); + SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax)); + SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum)); + SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin)); + SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum)); + SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul)); + SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd)); + SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub)); + SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX)); + SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd)); + SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub)); + SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate)); + SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused)); + SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX)); + SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate)); + SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused)); + SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt)); + SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub)); - var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert); - - SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate(dlgMathAbs)); - SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate(dlgMathCeiling)); - SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate(dlgMathFloor)); - SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate(dlgMathRound)); - SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate(dlgMathTruncate)); - - SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate(dlgMathFAbs)); - SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate(dlgMathFCeiling)); - SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate(dlgMathFFloor)); - SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate(dlgMathFRound)); - SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate(dlgMathFTruncate)); - - SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceBreak)); - SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceCheckSynchronization)); - SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceEnqueueForRejit)); - SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntfrqEl0)); - SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntpctEl0)); - SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntvctEl0)); - SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCtrEl0)); - SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetDczidEl0)); - SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetFunctionAddress)); - SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceInvalidateCacheLine)); - SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadByte)); - SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt16)); - SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt32)); - SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt64)); - SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadVector128)); - SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceSignalMemoryTracking)); - SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceSupervisorCall)); - SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceThrowInvalidMemoryAccess)); - SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceUndefined)); - SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteByte)); - SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt16)); - SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt32)); - SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt64)); - SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteVector128)); - - SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCountLeadingSigns)); - SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCountLeadingZeros)); - SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32b)); - SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cb)); - SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32ch)); - SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cw)); - SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cx)); - SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32h)); - SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32w)); - SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32x)); - SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackDecrypt)); - SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackEncrypt)); - SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackFixedRotate)); - SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashChoose)); - SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashLower)); - SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashMajority)); - SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashParity)); - SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashUpper)); - SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackInverseMixColumns)); - SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackMixColumns)); - SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackPolynomialMult64_128)); - SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToS32)); - SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToS64)); - SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToU32)); - SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToU64)); - SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToS32)); - SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToS64)); - SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToU32)); - SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToU64)); - SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha1SchedulePart1)); - SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha1SchedulePart2)); - SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha256SchedulePart1)); - SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha256SchedulePart2)); - SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSignedShrImm64)); - SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl1)); - SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl2)); - SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl3)); - SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl4)); - SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx1)); - SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx2)); - SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx3)); - SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx4)); - SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackUnsignedShrImm64)); - - SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat16_32FPConvert)); - SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat16_64FPConvert)); - - SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPAdd)); - SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPAddFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompare)); - SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareEQ)); - SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareEQFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGE)); - SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGEFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGT)); - SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGTFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLE)); - SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLEFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLT)); - SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLTFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPDiv)); - SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMax)); - SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxNum)); - SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxNumFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMin)); - SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinNum)); - SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinNumFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMul)); - SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulAdd)); - SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulAddFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulSub)); - SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulSubFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulX)); - SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPNegMulAdd)); - SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPNegMulSub)); - SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipEstimate)); - SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipStep)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipStepFused)); - SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecpX)); - SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRSqrtEstimate)); - SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRSqrtStep)); // A32 only. - SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRSqrtStepFused)); - SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPSqrt)); - SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPSub)); - - SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32_16FPConvert)); - - SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPAdd)); - SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPAddFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompare)); - SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareEQ)); - SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareEQFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareGE)); - SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareGEFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareGT)); - SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareGTFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareLE)); - SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareLEFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareLT)); - SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPCompareLTFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPDiv)); - SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMax)); - SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMaxFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMaxNum)); - SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMaxNumFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMin)); - SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMinFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMinNum)); - SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMinNumFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMul)); - SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulAdd)); - SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulAddFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulSub)); - SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulSubFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPMulX)); - SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPNegMulAdd)); - SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPNegMulSub)); - SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRecipEstimate)); - SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRecipStep)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRecipStepFused)); - SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRecpX)); - SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRSqrtEstimate)); - SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRSqrtStep)); // A32 only. - SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPRSqrtStepFused)); - SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPSqrt)); - SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64FPSub)); - - SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat64_16FPConvert)); + SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert)); } private delegate double MathAbs(double value); diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index c2eed7a55..f56bdce1c 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 6634; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/ARMeilleure/Translation/RegisterUsage.cs b/src/ARMeilleure/Translation/RegisterUsage.cs index 472b0f67b..c8c250626 100644 --- a/src/ARMeilleure/Translation/RegisterUsage.cs +++ b/src/ARMeilleure/Translation/RegisterUsage.cs @@ -89,17 +89,6 @@ namespace ARMeilleure.Translation public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode) { - if (cfg.Entry.Predecessors.Count != 0) - { - // We expect the entry block to have no predecessors. - // This is required because we have a implicit context load at the start of the function, - // but if there is a jump to the start of the function, the context load would trash the modified values. - // Here we insert a new entry block that will jump to the existing entry block. - BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count); - - cfg.UpdateEntry(newEntry); - } - // Compute local register inputs and outputs used inside blocks. RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count]; @@ -212,7 +201,7 @@ namespace ARMeilleure.Translation // The only block without any predecessor should be the entry block. // It always needs a context load as it is the first block to run. - if (block == cfg.Entry || hasContextLoad) + if (block.Predecessors.Count == 0 || hasContextLoad) { long vecMask = globalInputs[block.Index].VecMask; long intMask = globalInputs[block.Index].IntMask; diff --git a/src/ARMeilleure/Translation/TranslatorQueue.cs b/src/ARMeilleure/Translation/TranslatorQueue.cs index 831522bc1..cee2f9080 100644 --- a/src/ARMeilleure/Translation/TranslatorQueue.cs +++ b/src/ARMeilleure/Translation/TranslatorQueue.cs @@ -80,10 +80,7 @@ namespace ARMeilleure.Translation return true; } - if (!_disposed) - { - Monitor.Wait(Sync); - } + Monitor.Wait(Sync); } } diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 4eb75a578..62fe5025d 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -89,9 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2 return; } - using SpanOwner samplesOwner = SpanOwner.Rent(frameCount * _bytesPerFrame); + using IMemoryOwner samplesOwner = ByteMemoryPool.Rent(frameCount * _bytesPerFrame); - Span samples = samplesOwner.Span; + Span samples = samplesOwner.Memory.Span; _ringBuffer.Read(samples, 0, samples.Length); diff --git a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index e9cc6a8e1..4011a1214 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo int channelCount = areas.Length; - using SpanOwner samplesOwner = SpanOwner.Rent(frameCount * bytesPerFrame); + using IMemoryOwner samplesOwner = ByteMemoryPool.Rent(frameCount * bytesPerFrame); - Span samples = samplesOwner.Span; + Span samples = samplesOwner.Memory.Span; _ringBuffer.Read(samples, 0, samples.Length); diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs index 7aefe8865..b95e5bed1 100644 --- a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs +++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Backends.Common private readonly object _lock = new(); - private MemoryOwner _bufferOwner; + private IMemoryOwner _bufferOwner; private Memory _buffer; private int _size; private int _headOffset; @@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Backends.Common public DynamicRingBuffer(int initialCapacity = RingBufferAlignment) { - _bufferOwner = MemoryOwner.RentCleared(initialCapacity); + _bufferOwner = ByteMemoryPool.RentCleared(initialCapacity); _buffer = _bufferOwner.Memory; } @@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Backends.Common private void SetCapacityLocked(int capacity) { - MemoryOwner newBufferOwner = MemoryOwner.RentCleared(capacity); + IMemoryOwner newBufferOwner = ByteMemoryPool.RentCleared(capacity); Memory newBuffer = newBufferOwner.Memory; if (_size > 0) diff --git a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs b/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs index 7f881373f..608381af1 100644 --- a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs +++ b/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Audio.Renderer.Common { public const int Align = 0x10; public const int BiquadStateOffset = 0x0; + public const int BiquadStateSize = 0x10; /// /// The state of the biquad filters of this voice. diff --git a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs index 31f614d67..1a51a1fbd 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs @@ -16,15 +16,10 @@ namespace Ryujinx.Audio.Renderer.Dsp /// The biquad filter parameter /// The biquad filter state /// The output buffer to write the result - /// The input buffer to read the samples from + /// The input buffer to write the result /// The count of samples to process [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ProcessBiquadFilter( - ref BiquadFilterParameter parameter, - ref BiquadFilterState state, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount) + public static void ProcessBiquadFilter(ref BiquadFilterParameter parameter, ref BiquadFilterState state, Span outputBuffer, ReadOnlySpan inputBuffer, uint sampleCount) { float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); @@ -45,96 +40,6 @@ namespace Ryujinx.Audio.Renderer.Dsp } } - /// - /// Apply a single biquad filter and mix the result into the output buffer. - /// - /// This is implemented with a direct form 1. - /// The biquad filter parameter - /// The biquad filter state - /// The output buffer to write the result - /// The input buffer to read the samples from - /// The count of samples to process - /// Mix volume - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ProcessBiquadFilterAndMix( - ref BiquadFilterParameter parameter, - ref BiquadFilterState state, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount, - float volume) - { - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); - - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); - - for (int i = 0; i < sampleCount; i++) - { - float input = inputBuffer[i]; - float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2; - - state.State1 = state.State0; - state.State0 = input; - state.State3 = state.State2; - state.State2 = output; - - outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume); - } - } - - /// - /// Apply a single biquad filter and mix the result into the output buffer with volume ramp. - /// - /// This is implemented with a direct form 1. - /// The biquad filter parameter - /// The biquad filter state - /// The output buffer to write the result - /// The input buffer to read the samples from - /// The count of samples to process - /// Initial mix volume - /// Volume increment step - /// Last filtered sample value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float ProcessBiquadFilterAndMixRamp( - ref BiquadFilterParameter parameter, - ref BiquadFilterState state, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount, - float volume, - float ramp) - { - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); - - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); - - float mixState = 0f; - - for (int i = 0; i < sampleCount; i++) - { - float input = inputBuffer[i]; - float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2; - - state.State1 = state.State0; - state.State0 = input; - state.State3 = state.State2; - state.State2 = output; - - mixState = FloatingPointHelper.MultiplyRoundUp(output, volume); - - outputBuffer[i] += mixState; - volume += ramp; - } - - return mixState; - } - /// /// Apply multiple biquad filter. /// @@ -142,15 +47,10 @@ namespace Ryujinx.Audio.Renderer.Dsp /// The biquad filter parameter /// The biquad filter state /// The output buffer to write the result - /// The input buffer to read the samples from + /// The input buffer to write the result /// The count of samples to process [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ProcessBiquadFilter( - ReadOnlySpan parameters, - Span states, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount) + public static void ProcessBiquadFilter(ReadOnlySpan parameters, Span states, Span outputBuffer, ReadOnlySpan inputBuffer, uint sampleCount) { for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++) { @@ -167,7 +67,7 @@ namespace Ryujinx.Audio.Renderer.Dsp for (int i = 0; i < sampleCount; i++) { - float input = stageIndex != 0 ? outputBuffer[i] : inputBuffer[i]; + float input = inputBuffer[i]; float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2; state.State1 = state.State0; @@ -179,129 +79,5 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } - - /// - /// Apply double biquad filter and mix the result into the output buffer. - /// - /// This is implemented with a direct form 1. - /// The biquad filter parameter - /// The biquad filter state - /// The output buffer to write the result - /// The input buffer to read the samples from - /// The count of samples to process - /// Mix volume - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ProcessDoubleBiquadFilterAndMix( - ref BiquadFilterParameter parameter0, - ref BiquadFilterParameter parameter1, - ref BiquadFilterState state0, - ref BiquadFilterState state1, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount, - float volume) - { - float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); - - float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); - - float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); - - float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); - - for (int i = 0; i < sampleCount; i++) - { - float input = inputBuffer[i]; - float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20; - - state0.State1 = state0.State0; - state0.State0 = input; - state0.State3 = state0.State2; - state0.State2 = output; - - input = output; - output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21; - - state1.State1 = state1.State0; - state1.State0 = input; - state1.State3 = state1.State2; - state1.State2 = output; - - outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume); - } - } - - /// - /// Apply double biquad filter and mix the result into the output buffer with volume ramp. - /// - /// This is implemented with a direct form 1. - /// The biquad filter parameter - /// The biquad filter state - /// The output buffer to write the result - /// The input buffer to read the samples from - /// The count of samples to process - /// Initial mix volume - /// Volume increment step - /// Last filtered sample value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float ProcessDoubleBiquadFilterAndMixRamp( - ref BiquadFilterParameter parameter0, - ref BiquadFilterParameter parameter1, - ref BiquadFilterState state0, - ref BiquadFilterState state1, - Span outputBuffer, - ReadOnlySpan inputBuffer, - uint sampleCount, - float volume, - float ramp) - { - float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); - - float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); - - float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); - - float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); - - float mixState = 0f; - - for (int i = 0; i < sampleCount; i++) - { - float input = inputBuffer[i]; - float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20; - - state0.State1 = state0.State0; - state0.State0 = input; - state0.State3 = state0.State2; - state0.State2 = output; - - input = output; - output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21; - - state1.State1 = state1.State0; - state1.State0 = input; - state1.State3 = state1.State2; - state1.State2 = output; - - mixState = FloatingPointHelper.MultiplyRoundUp(output, volume); - - outputBuffer[i] += mixState; - volume += ramp; - } - - return mixState; - } } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs deleted file mode 100644 index 106fc0357..000000000 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Ryujinx.Audio.Renderer.Common; -using Ryujinx.Audio.Renderer.Dsp.State; -using Ryujinx.Audio.Renderer.Parameter; -using System; - -namespace Ryujinx.Audio.Renderer.Dsp.Command -{ - public class BiquadFilterAndMixCommand : ICommand - { - public bool Enabled { get; set; } - - public int NodeId { get; } - - public CommandType CommandType => CommandType.BiquadFilterAndMix; - - public uint EstimatedProcessingTime { get; set; } - - public ushort InputBufferIndex { get; } - public ushort OutputBufferIndex { get; } - - private BiquadFilterParameter _parameter; - - public Memory BiquadFilterState { get; } - public Memory PreviousBiquadFilterState { get; } - - public Memory State { get; } - - public int LastSampleIndex { get; } - - public float Volume0 { get; } - public float Volume1 { get; } - - public bool NeedInitialization { get; } - public bool HasVolumeRamp { get; } - public bool IsFirstMixBuffer { get; } - - public BiquadFilterAndMixCommand( - float volume0, - float volume1, - uint inputBufferIndex, - uint outputBufferIndex, - int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter, - Memory biquadFilterState, - Memory previousBiquadFilterState, - bool needInitialization, - bool hasVolumeRamp, - bool isFirstMixBuffer, - int nodeId) - { - Enabled = true; - NodeId = nodeId; - - InputBufferIndex = (ushort)inputBufferIndex; - OutputBufferIndex = (ushort)outputBufferIndex; - - _parameter = filter; - BiquadFilterState = biquadFilterState; - PreviousBiquadFilterState = previousBiquadFilterState; - - State = state; - LastSampleIndex = lastSampleIndex; - - Volume0 = volume0; - Volume1 = volume1; - - NeedInitialization = needInitialization; - HasVolumeRamp = hasVolumeRamp; - IsFirstMixBuffer = isFirstMixBuffer; - } - - public void Process(CommandList context) - { - ReadOnlySpan inputBuffer = context.GetBuffer(InputBufferIndex); - Span outputBuffer = context.GetBuffer(OutputBufferIndex); - - if (NeedInitialization) - { - // If there is no previous state, initialize to zero. - - BiquadFilterState.Span[0] = new BiquadFilterState(); - } - else if (IsFirstMixBuffer) - { - // This is the first buffer, set previous state to current state. - - PreviousBiquadFilterState.Span[0] = BiquadFilterState.Span[0]; - } - else - { - // Rewind the current state by copying back the previous state. - - BiquadFilterState.Span[0] = PreviousBiquadFilterState.Span[0]; - } - - if (HasVolumeRamp) - { - float volume = Volume0; - float ramp = (Volume1 - Volume0) / (int)context.SampleCount; - - State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessBiquadFilterAndMixRamp( - ref _parameter, - ref BiquadFilterState.Span[0], - outputBuffer, - inputBuffer, - context.SampleCount, - volume, - ramp); - } - else - { - BiquadFilterHelper.ProcessBiquadFilterAndMix( - ref _parameter, - ref BiquadFilterState.Span[0], - outputBuffer, - inputBuffer, - context.SampleCount, - Volume1); - } - } - } -} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs index de5c0ea2c..098a04a04 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs @@ -30,10 +30,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command CopyMixBuffer, LimiterVersion1, LimiterVersion2, - MultiTapBiquadFilter, + GroupedBiquadFilter, CaptureBuffer, Compressor, - BiquadFilterAndMix, - MultiTapBiquadFilterAndMix, } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs similarity index 84% rename from src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs index e159f8ef7..7af851bdc 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs @@ -4,13 +4,13 @@ using System; namespace Ryujinx.Audio.Renderer.Dsp.Command { - public class MultiTapBiquadFilterCommand : ICommand + public class GroupedBiquadFilterCommand : ICommand { public bool Enabled { get; set; } public int NodeId { get; } - public CommandType CommandType => CommandType.MultiTapBiquadFilter; + public CommandType CommandType => CommandType.GroupedBiquadFilter; public uint EstimatedProcessingTime { get; set; } @@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private readonly int _outputBufferIndex; private readonly bool[] _isInitialized; - public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) + public GroupedBiquadFilterCommand(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { _parameters = filters.ToArray(); _biquadFilterStates = biquadFilterStateMemory; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs index 41ac84c1a..3c7dd63b2 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs @@ -24,14 +24,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public Memory State { get; } - public MixRampGroupedCommand( - uint mixBufferCount, - uint inputBufferIndex, - uint outputBufferIndex, - ReadOnlySpan volume0, - ReadOnlySpan volume1, - Memory state, - int nodeId) + public MixRampGroupedCommand(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span volume0, Span volume1, Memory state, int nodeId) { Enabled = true; MixBufferCount = mixBufferCount; @@ -55,12 +48,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float ProcessMixRampGrouped( - Span outputBuffer, - ReadOnlySpan inputBuffer, - float volume0, - float volume1, - int sampleCount) + private static float ProcessMixRampGrouped(Span outputBuffer, ReadOnlySpan inputBuffer, float volume0, float volume1, int sampleCount) { float ramp = (volume1 - volume0) / sampleCount; float volume = volume0; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs deleted file mode 100644 index e359371b4..000000000 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Ryujinx.Audio.Renderer.Common; -using Ryujinx.Audio.Renderer.Dsp.State; -using Ryujinx.Audio.Renderer.Parameter; -using System; - -namespace Ryujinx.Audio.Renderer.Dsp.Command -{ - public class MultiTapBiquadFilterAndMixCommand : ICommand - { - public bool Enabled { get; set; } - - public int NodeId { get; } - - public CommandType CommandType => CommandType.MultiTapBiquadFilterAndMix; - - public uint EstimatedProcessingTime { get; set; } - - public ushort InputBufferIndex { get; } - public ushort OutputBufferIndex { get; } - - private BiquadFilterParameter _parameter0; - private BiquadFilterParameter _parameter1; - - public Memory BiquadFilterState0 { get; } - public Memory BiquadFilterState1 { get; } - public Memory PreviousBiquadFilterState0 { get; } - public Memory PreviousBiquadFilterState1 { get; } - - public Memory State { get; } - - public int LastSampleIndex { get; } - - public float Volume0 { get; } - public float Volume1 { get; } - - public bool NeedInitialization0 { get; } - public bool NeedInitialization1 { get; } - public bool HasVolumeRamp { get; } - public bool IsFirstMixBuffer { get; } - - public MultiTapBiquadFilterAndMixCommand( - float volume0, - float volume1, - uint inputBufferIndex, - uint outputBufferIndex, - int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter0, - ref BiquadFilterParameter filter1, - Memory biquadFilterState0, - Memory biquadFilterState1, - Memory previousBiquadFilterState0, - Memory previousBiquadFilterState1, - bool needInitialization0, - bool needInitialization1, - bool hasVolumeRamp, - bool isFirstMixBuffer, - int nodeId) - { - Enabled = true; - NodeId = nodeId; - - InputBufferIndex = (ushort)inputBufferIndex; - OutputBufferIndex = (ushort)outputBufferIndex; - - _parameter0 = filter0; - _parameter1 = filter1; - BiquadFilterState0 = biquadFilterState0; - BiquadFilterState1 = biquadFilterState1; - PreviousBiquadFilterState0 = previousBiquadFilterState0; - PreviousBiquadFilterState1 = previousBiquadFilterState1; - - State = state; - LastSampleIndex = lastSampleIndex; - - Volume0 = volume0; - Volume1 = volume1; - - NeedInitialization0 = needInitialization0; - NeedInitialization1 = needInitialization1; - HasVolumeRamp = hasVolumeRamp; - IsFirstMixBuffer = isFirstMixBuffer; - } - - private void UpdateState(Memory state, Memory previousState, bool needInitialization) - { - if (needInitialization) - { - // If there is no previous state, initialize to zero. - - state.Span[0] = new BiquadFilterState(); - } - else if (IsFirstMixBuffer) - { - // This is the first buffer, set previous state to current state. - - previousState.Span[0] = state.Span[0]; - } - else - { - // Rewind the current state by copying back the previous state. - - state.Span[0] = previousState.Span[0]; - } - } - - public void Process(CommandList context) - { - ReadOnlySpan inputBuffer = context.GetBuffer(InputBufferIndex); - Span outputBuffer = context.GetBuffer(OutputBufferIndex); - - UpdateState(BiquadFilterState0, PreviousBiquadFilterState0, NeedInitialization0); - UpdateState(BiquadFilterState1, PreviousBiquadFilterState1, NeedInitialization1); - - if (HasVolumeRamp) - { - float volume = Volume0; - float ramp = (Volume1 - Volume0) / (int)context.SampleCount; - - State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessDoubleBiquadFilterAndMixRamp( - ref _parameter0, - ref _parameter1, - ref BiquadFilterState0.Span[0], - ref BiquadFilterState1.Span[0], - outputBuffer, - inputBuffer, - context.SampleCount, - volume, - ramp); - } - else - { - BiquadFilterHelper.ProcessDoubleBiquadFilterAndMix( - ref _parameter0, - ref _parameter1, - ref BiquadFilterState0.Span[0], - ref BiquadFilterState1.Span[0], - outputBuffer, - inputBuffer, - context.SampleCount, - Volume1); - } - } - } -} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs index 58a2d9cce..f9a32b3f9 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs @@ -2,16 +2,12 @@ using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Dsp.State { - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x20)] + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] public struct BiquadFilterState { public float State0; public float State1; public float State2; public float State3; - public float State4; - public float State5; - public float State6; - public float State7; } } diff --git a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs deleted file mode 100644 index 807232f20..000000000 --- a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Ryujinx.Common.Memory; -using System; - -namespace Ryujinx.Audio.Renderer.Parameter -{ - /// - /// Generic interface for the splitter destination parameters. - /// - public interface ISplitterDestinationInParameter - { - /// - /// Target splitter destination data id. - /// - int Id { get; } - - /// - /// The mix to output the result of the splitter. - /// - int DestinationId { get; } - - /// - /// Biquad filter parameters. - /// - Array2 BiquadFilters { get; } - - /// - /// Set to true if in use. - /// - bool IsUsed { get; } - - /// - /// Mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - Span MixBufferVolume { get; } - - /// - /// Check if the magic is valid. - /// - /// Returns true if the magic is valid. - bool IsMagicValid(); - } -} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs similarity index 73% rename from src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs index 029c001ea..b74b67be0 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using System; using System.Runtime.InteropServices; @@ -6,10 +5,10 @@ using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Parameter { /// - /// Input header for a splitter destination version 1 update. + /// Input header for a splitter destination update. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SplitterDestinationInParameterVersion1 : ISplitterDestinationInParameter + public struct SplitterDestinationInParameter { /// /// Magic of the input header. @@ -42,7 +41,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// private unsafe fixed byte _reserved[3]; - [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] + [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)] private struct MixArray { } /// @@ -51,14 +50,6 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Used when a splitter id is specified in the mix. public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mixBufferVolume); - readonly int ISplitterDestinationInParameter.Id => Id; - - readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; - - readonly Array2 ISplitterDestinationInParameter.BiquadFilters => default; - - readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; - /// /// The expected constant of any input header. /// diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs deleted file mode 100644 index 312be8b70..000000000 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Common.Utilities; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Audio.Renderer.Parameter -{ - /// - /// Input header for a splitter destination version 2 update. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SplitterDestinationInParameterVersion2 : ISplitterDestinationInParameter - { - /// - /// Magic of the input header. - /// - public uint Magic; - - /// - /// Target splitter destination data id. - /// - public int Id; - - /// - /// Mix buffer volumes storage. - /// - private MixArray _mixBufferVolume; - - /// - /// The mix to output the result of the splitter. - /// - public int DestinationId; - - /// - /// Biquad filter parameters. - /// - public Array2 BiquadFilters; - - /// - /// Set to true if in use. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsUsed; - - /// - /// Reserved/padding. - /// - private unsafe fixed byte _reserved[11]; - - [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] - private struct MixArray { } - - /// - /// Mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mixBufferVolume); - - readonly int ISplitterDestinationInParameter.Id => Id; - - readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; - - readonly Array2 ISplitterDestinationInParameter.BiquadFilters => BiquadFilters; - - readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; - - /// - /// The expected constant of any input header. - /// - private const uint ValidMagic = 0x44444E53; - - /// - /// Check if the magic is valid. - /// - /// Returns true if the magic is valid. - public readonly bool IsMagicValid() - { - return Magic == ValidMagic; - } - } -} diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 246889c48..9b56f5cbd 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -1,7 +1,6 @@ using Ryujinx.Audio.Integration; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Dsp.Command; -using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Server.Effect; using Ryujinx.Audio.Renderer.Server.MemoryPool; @@ -174,22 +173,6 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.WorkBufferTooSmall; } - Memory splitterBqfStates = Memory.Empty; - - if (_behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && - parameter.SplitterCount > 0 && - parameter.SplitterDestinationCount > 0) - { - splitterBqfStates = workBufferAllocator.Allocate(parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10); - - if (splitterBqfStates.IsEmpty) - { - return ResultCode.WorkBufferTooSmall; - } - - splitterBqfStates.Span.Clear(); - } - // Invalidate DSP cache on what was currently allocated with workBuffer. AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset); @@ -309,7 +292,7 @@ namespace Ryujinx.Audio.Renderer.Server state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); } - if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator, splitterBqfStates)) + if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator)) { return ResultCode.WorkBufferTooSmall; } @@ -792,13 +775,6 @@ namespace Ryujinx.Audio.Renderer.Server // Splitter size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter); - if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && - parameter.SplitterCount > 0 && - parameter.SplitterDestinationCount > 0) - { - size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10); - } - // DSP Voice size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceUpdateState.Align); diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index 32c7de6cf..fe1dfc4be 100644 --- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Audio.Renderer.Server /// was added to supply the count of update done sent to the DSP. /// A new version of the command estimator was added to address timing changes caused by the voice changes. /// Additionally, the rendering limit percent was incremented to 80%. + /// /// /// This was added in system update 6.0.0 public const int Revision5 = 5 << 24; @@ -100,18 +101,10 @@ namespace Ryujinx.Audio.Renderer.Server /// This was added in system update 14.0.0 but some changes were made in 15.0.0 public const int Revision11 = 11 << 24; - /// - /// REV12: - /// Two new commands were added to for biquad filtering and mixing (with optinal volume ramp) on the same command. - /// Splitter destinations can now specify up to two biquad filtering parameters, used for filtering the buffer before mixing. - /// - /// This was added in system update 17.0.0 - public const int Revision12 = 12 << 24; - /// /// Last revision supported by the implementation. /// - public const int LastRevision = Revision12; + public const int LastRevision = Revision11; /// /// Target revision magic supported by the implementation. @@ -361,7 +354,7 @@ namespace Ryujinx.Audio.Renderer.Server /// Check if the audio renderer should use an optimized Biquad Filter (Direct Form 1) in case of two biquad filters are defined on a voice. /// /// True if the audio renderer should use the optimization. - public bool UseMultiTapBiquadFilterProcessing() + public bool IsBiquadFilterGroupedOptimizationSupported() { return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10); } @@ -375,15 +368,6 @@ namespace Ryujinx.Audio.Renderer.Server return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11); } - /// - /// Check if the audio renderer should support biquad filter on splitter. - /// - /// True if the audio renderer support biquad filter on splitter - public bool IsBiquadFilterParameterForSplitterEnabled() - { - return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12); - } - /// /// Get the version of the . /// diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 702f05462..f4174a913 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -204,7 +204,7 @@ namespace Ryujinx.Audio.Renderer.Server } /// - /// Create a new . + /// Create a new . /// /// The base index of the input and output buffer. /// The biquad filter parameters. @@ -213,9 +213,9 @@ namespace Ryujinx.Audio.Renderer.Server /// The output buffer offset. /// Set to true if the biquad filter state is initialized. /// The node id associated to this command. - public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) + public void GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { - MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); + GroupedBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -232,7 +232,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The new volume. /// The to generate the command from. /// The node id associated to this command. - public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan previousVolume, ReadOnlySpan volume, Memory state, int nodeId) + public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span previousVolume, Span volume, Memory state, int nodeId) { MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); @@ -260,120 +260,6 @@ namespace Ryujinx.Audio.Renderer.Server AddCommand(command); } - /// - /// Generate a new . - /// - /// The previous volume. - /// The new volume. - /// The input buffer index. - /// The output buffer index. - /// The index in the array to store the ramped sample. - /// The to generate the command from. - /// The biquad filter parameter. - /// The biquad state. - /// The previous biquad state. - /// Set to true if the biquad filter state needs to be initialized. - /// Set to true if the mix has volume ramp, and should be taken into account. - /// Set to true if the buffer is the first mix buffer. - /// The node id associated to this command. - public void GenerateBiquadFilterAndMix( - float previousVolume, - float volume, - uint inputBufferIndex, - uint outputBufferIndex, - int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter, - Memory biquadFilterState, - Memory previousBiquadFilterState, - bool needInitialization, - bool hasVolumeRamp, - bool isFirstMixBuffer, - int nodeId) - { - BiquadFilterAndMixCommand command = new( - previousVolume, - volume, - inputBufferIndex, - outputBufferIndex, - lastSampleIndex, - state, - ref filter, - biquadFilterState, - previousBiquadFilterState, - needInitialization, - hasVolumeRamp, - isFirstMixBuffer, - nodeId); - - command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); - - AddCommand(command); - } - - /// - /// Generate a new . - /// - /// The previous volume. - /// The new volume. - /// The input buffer index. - /// The output buffer index. - /// The index in the array to store the ramped sample. - /// The to generate the command from. - /// First biquad filter parameter. - /// Second biquad filter parameter. - /// First biquad state. - /// Second biquad state. - /// First previous biquad state. - /// Second previous biquad state. - /// Set to true if the first biquad filter state needs to be initialized. - /// Set to true if the second biquad filter state needs to be initialized. - /// Set to true if the mix has volume ramp, and should be taken into account. - /// Set to true if the buffer is the first mix buffer. - /// The node id associated to this command. - public void GenerateMultiTapBiquadFilterAndMix( - float previousVolume, - float volume, - uint inputBufferIndex, - uint outputBufferIndex, - int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter0, - ref BiquadFilterParameter filter1, - Memory biquadFilterState0, - Memory biquadFilterState1, - Memory previousBiquadFilterState0, - Memory previousBiquadFilterState1, - bool needInitialization0, - bool needInitialization1, - bool hasVolumeRamp, - bool isFirstMixBuffer, - int nodeId) - { - MultiTapBiquadFilterAndMixCommand command = new( - previousVolume, - volume, - inputBufferIndex, - outputBufferIndex, - lastSampleIndex, - state, - ref filter0, - ref filter1, - biquadFilterState0, - biquadFilterState1, - previousBiquadFilterState0, - previousBiquadFilterState1, - needInitialization0, - needInitialization1, - hasVolumeRamp, - isFirstMixBuffer, - nodeId); - - command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); - - AddCommand(command); - } - /// /// Generate a new . /// @@ -382,7 +268,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The buffer count. /// The node id associated to this command. /// The target sample rate in use. - public void GenerateDepopForMixBuffers(Memory depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate) + public void GenerateDepopForMixBuffersCommand(Memory depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate) { DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index d798230c1..ae8f699f3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -12,7 +12,6 @@ using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Utils; using System; using System.Diagnostics; -using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Server { @@ -47,13 +46,12 @@ namespace Ryujinx.Audio.Renderer.Server { ref MixState mix = ref _mixContext.GetState(voiceState.MixId); - _commandBuffer.GenerateDepopPrepare( - dspState, - _rendererContext.DepopBuffer, - mix.BufferCount, - mix.BufferOffset, - voiceState.NodeId, - voiceState.WasPlaying); + _commandBuffer.GenerateDepopPrepare(dspState, + _rendererContext.DepopBuffer, + mix.BufferCount, + mix.BufferOffset, + voiceState.NodeId, + voiceState.WasPlaying); } else if (voiceState.SplitterId != Constants.UnusedSplitterId) { @@ -61,13 +59,15 @@ namespace Ryujinx.Audio.Renderer.Server while (true) { - SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++); + Span destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++); - if (destination.IsNull) + if (destinationSpan.IsEmpty) { break; } + ref SplitterDestination destination = ref destinationSpan[0]; + if (destination.IsConfigured()) { int mixId = destination.DestinationId; @@ -76,13 +76,12 @@ namespace Ryujinx.Audio.Renderer.Server { ref MixState mix = ref _mixContext.GetState(mixId); - _commandBuffer.GenerateDepopPrepare( - dspState, - _rendererContext.DepopBuffer, - mix.BufferCount, - mix.BufferOffset, - voiceState.NodeId, - voiceState.WasPlaying); + _commandBuffer.GenerateDepopPrepare(dspState, + _rendererContext.DepopBuffer, + mix.BufferCount, + mix.BufferOffset, + voiceState.NodeId, + voiceState.WasPlaying); destination.MarkAsNeedToUpdateInternalState(); } @@ -96,39 +95,35 @@ namespace Ryujinx.Audio.Renderer.Server if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported()) { - _commandBuffer.GenerateDataSourceVersion2( - ref voiceState, - dspState, - (ushort)_rendererContext.MixBufferCount, - (ushort)channelIndex, - voiceState.NodeId); + _commandBuffer.GenerateDataSourceVersion2(ref voiceState, + dspState, + (ushort)_rendererContext.MixBufferCount, + (ushort)channelIndex, + voiceState.NodeId); } else { switch (voiceState.SampleFormat) { case SampleFormat.PcmInt16: - _commandBuffer.GeneratePcmInt16DataSourceVersion1( - ref voiceState, - dspState, - (ushort)_rendererContext.MixBufferCount, - (ushort)channelIndex, - voiceState.NodeId); + _commandBuffer.GeneratePcmInt16DataSourceVersion1(ref voiceState, + dspState, + (ushort)_rendererContext.MixBufferCount, + (ushort)channelIndex, + voiceState.NodeId); break; case SampleFormat.PcmFloat: - _commandBuffer.GeneratePcmFloatDataSourceVersion1( - ref voiceState, - dspState, - (ushort)_rendererContext.MixBufferCount, - (ushort)channelIndex, - voiceState.NodeId); + _commandBuffer.GeneratePcmFloatDataSourceVersion1(ref voiceState, + dspState, + (ushort)_rendererContext.MixBufferCount, + (ushort)channelIndex, + voiceState.NodeId); break; case SampleFormat.Adpcm: - _commandBuffer.GenerateAdpcmDataSourceVersion1( - ref voiceState, - dspState, - (ushort)_rendererContext.MixBufferCount, - voiceState.NodeId); + _commandBuffer.GenerateAdpcmDataSourceVersion1(ref voiceState, + dspState, + (ushort)_rendererContext.MixBufferCount, + voiceState.NodeId); break; default: throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}"); @@ -139,14 +134,14 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory state, int baseIndex, int bufferOffset, int nodeId) { - bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing(); + bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported(); if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable) { - Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(Unsafe.SizeOf() * Constants.VoiceBiquadFilterCount)]; + Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)]; Memory stateMemory = SpanMemoryManager.Cast(biquadStateRawMemory); - _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); + _commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); } else { @@ -156,134 +151,33 @@ namespace Ryujinx.Audio.Renderer.Server if (filter.Enable) { - Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(Unsafe.SizeOf() * Constants.VoiceBiquadFilterCount)]; + Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)]; + Memory stateMemory = SpanMemoryManager.Cast(biquadStateRawMemory); - _commandBuffer.GenerateBiquadFilter( - baseIndex, - ref filter, - stateMemory.Slice(i, 1), - bufferOffset, - bufferOffset, - !voiceState.BiquadFilterNeedInitialization[i], - nodeId); + _commandBuffer.GenerateBiquadFilter(baseIndex, + ref filter, + stateMemory.Slice(i, 1), + bufferOffset, + bufferOffset, + !voiceState.BiquadFilterNeedInitialization[i], + nodeId); } } } } - private void GenerateVoiceMixWithSplitter( - SplitterDestination destination, - Memory state, - uint bufferOffset, - uint bufferCount, - uint bufferIndex, - int nodeId) - { - ReadOnlySpan mixVolumes = destination.MixBufferVolume; - ReadOnlySpan previousMixVolumes = destination.PreviousMixBufferVolume; - - ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); - ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); - - Memory bqfState = _splitterContext.GetBiquadFilterState(destination); - - bool isFirstMixBuffer = true; - - for (int i = 0; i < bufferCount; i++) - { - float previousMixVolume = previousMixVolumes[i]; - float mixVolume = mixVolumes[i]; - - if (mixVolume != 0.0f || previousMixVolume != 0.0f) - { - if (bqf0.Enable && bqf1.Enable) - { - _commandBuffer.GenerateMultiTapBiquadFilterAndMix( - previousMixVolume, - mixVolume, - bufferIndex, - bufferOffset + (uint)i, - i, - state, - ref bqf0, - ref bqf1, - bqfState[..1], - bqfState.Slice(1, 1), - bqfState.Slice(2, 1), - bqfState.Slice(3, 1), - !destination.IsBiquadFilterEnabledPrev(), - !destination.IsBiquadFilterEnabledPrev(), - true, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(0); - destination.UpdateBiquadFilterEnabledPrev(1); - } - else if (bqf0.Enable) - { - _commandBuffer.GenerateBiquadFilterAndMix( - previousMixVolume, - mixVolume, - bufferIndex, - bufferOffset + (uint)i, - i, - state, - ref bqf0, - bqfState[..1], - bqfState.Slice(1, 1), - !destination.IsBiquadFilterEnabledPrev(), - true, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(0); - } - else if (bqf1.Enable) - { - _commandBuffer.GenerateBiquadFilterAndMix( - previousMixVolume, - mixVolume, - bufferIndex, - bufferOffset + (uint)i, - i, - state, - ref bqf1, - bqfState[..1], - bqfState.Slice(1, 1), - !destination.IsBiquadFilterEnabledPrev(), - true, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(1); - } - - isFirstMixBuffer = false; - } - } - } - - private void GenerateVoiceMix( - ReadOnlySpan mixVolumes, - ReadOnlySpan previousMixVolumes, - Memory state, - uint bufferOffset, - uint bufferCount, - uint bufferIndex, - int nodeId) + private void GenerateVoiceMix(Span mixVolumes, Span previousMixVolumes, Memory state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId) { if (bufferCount > Constants.VoiceChannelCountMax) { - _commandBuffer.GenerateMixRampGrouped( - bufferCount, - bufferIndex, - bufferOffset, - previousMixVolumes, - mixVolumes, - state, - nodeId); + _commandBuffer.GenerateMixRampGrouped(bufferCount, + bufferIndex, + bufferOffset, + previousMixVolumes, + mixVolumes, + state, + nodeId); } else { @@ -294,14 +188,13 @@ namespace Ryujinx.Audio.Renderer.Server if (mixVolume != 0.0f || previousMixVolume != 0.0f) { - _commandBuffer.GenerateMixRamp( - previousMixVolume, - mixVolume, - bufferIndex, - bufferOffset + (uint)i, - i, - state, - nodeId); + _commandBuffer.GenerateMixRamp(previousMixVolume, + mixVolume, + bufferIndex, + bufferOffset + (uint)i, + i, + state, + nodeId); } } } @@ -378,11 +271,10 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - _commandBuffer.GenerateVolumeRamp( - voiceState.PreviousVolume, - voiceState.Volume, - _rendererContext.MixBufferCount + (uint)channelIndex, - nodeId); + _commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume, + voiceState.Volume, + _rendererContext.MixBufferCount + (uint)channelIndex, + nodeId); if (performanceInitialized) { @@ -399,13 +291,15 @@ namespace Ryujinx.Audio.Renderer.Server while (true) { - SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId); + Span destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId); - if (destination.IsNull) + if (destinationSpan.IsEmpty) { break; } + ref SplitterDestination destination = ref destinationSpan[0]; + destinationId += (int)channelsCount; if (destination.IsConfigured()) @@ -416,27 +310,13 @@ namespace Ryujinx.Audio.Renderer.Server { ref MixState mix = ref _mixContext.GetState(mixId); - if (destination.IsBiquadFilterEnabled()) - { - GenerateVoiceMixWithSplitter( - destination, - dspStateMemory, - mix.BufferOffset, - mix.BufferCount, - _rendererContext.MixBufferCount + (uint)channelIndex, - nodeId); - } - else - { - GenerateVoiceMix( - destination.MixBufferVolume, - destination.PreviousMixBufferVolume, - dspStateMemory, - mix.BufferOffset, - mix.BufferCount, - _rendererContext.MixBufferCount + (uint)channelIndex, - nodeId); - } + GenerateVoiceMix(destination.MixBufferVolume, + destination.PreviousMixBufferVolume, + dspStateMemory, + mix.BufferOffset, + mix.BufferCount, + _rendererContext.MixBufferCount + (uint)channelIndex, + nodeId); destination.MarkAsNeedToUpdateInternalState(); } @@ -457,14 +337,13 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - GenerateVoiceMix( - channelResource.Mix.AsSpan(), - channelResource.PreviousMix.AsSpan(), - dspStateMemory, - mix.BufferOffset, - mix.BufferCount, - _rendererContext.MixBufferCount + (uint)channelIndex, - nodeId); + GenerateVoiceMix(channelResource.Mix.AsSpan(), + channelResource.PreviousMix.AsSpan(), + dspStateMemory, + mix.BufferOffset, + mix.BufferCount, + _rendererContext.MixBufferCount + (uint)channelIndex, + nodeId); if (performanceInitialized) { @@ -530,11 +409,10 @@ namespace Ryujinx.Audio.Renderer.Server { if (effect.Parameter.Volumes[i] != 0.0f) { - _commandBuffer.GenerateMix( - (uint)bufferOffset + effect.Parameter.Input[i], - (uint)bufferOffset + effect.Parameter.Output[i], - nodeId, - effect.Parameter.Volumes[i]); + _commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i], + (uint)bufferOffset + effect.Parameter.Output[i], + nodeId, + effect.Parameter.Volumes[i]); } } } @@ -569,18 +447,17 @@ namespace Ryujinx.Audio.Renderer.Server updateCount = newUpdateCount; } - _commandBuffer.GenerateAuxEffect( - bufferOffset, - effect.Parameter.Input[i], - effect.Parameter.Output[i], - ref effect.State, - effect.IsEnabled, - effect.Parameter.BufferStorageSize, - effect.State.SendBufferInfoBase, - effect.State.ReturnBufferInfoBase, - updateCount, - writeOffset, - nodeId); + _commandBuffer.GenerateAuxEffect(bufferOffset, + effect.Parameter.Input[i], + effect.Parameter.Output[i], + ref effect.State, + effect.IsEnabled, + effect.Parameter.BufferStorageSize, + effect.State.SendBufferInfoBase, + effect.State.ReturnBufferInfoBase, + updateCount, + writeOffset, + nodeId); writeOffset = newUpdateCount; @@ -623,7 +500,7 @@ namespace Ryujinx.Audio.Renderer.Server if (effect.IsEnabled) { bool needInitialization = effect.Parameter.Status == UsageState.Invalid || - (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed()); + (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed()); BiquadFilterParameter parameter = new() { @@ -635,14 +512,11 @@ namespace Ryujinx.Audio.Renderer.Server for (int i = 0; i < effect.Parameter.ChannelCount; i++) { - _commandBuffer.GenerateBiquadFilter( - (int)bufferOffset, - ref parameter, - effect.State.Slice(i, 1), - effect.Parameter.Input[i], - effect.Parameter.Output[i], - needInitialization, - nodeId); + _commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1), + effect.Parameter.Input[i], + effect.Parameter.Output[i], + needInitialization, + nodeId); } } else @@ -717,16 +591,15 @@ namespace Ryujinx.Audio.Renderer.Server updateCount = newUpdateCount; } - _commandBuffer.GenerateCaptureEffect( - bufferOffset, - effect.Parameter.Input[i], - effect.State.SendBufferInfo, - effect.IsEnabled, - effect.Parameter.BufferStorageSize, - effect.State.SendBufferInfoBase, - updateCount, - writeOffset, - nodeId); + _commandBuffer.GenerateCaptureEffect(bufferOffset, + effect.Parameter.Input[i], + effect.State.SendBufferInfo, + effect.IsEnabled, + effect.Parameter.BufferStorageSize, + effect.State.SendBufferInfoBase, + updateCount, + writeOffset, + nodeId); writeOffset = newUpdateCount; @@ -739,12 +612,11 @@ namespace Ryujinx.Audio.Renderer.Server { Debug.Assert(effect.Type == EffectType.Compressor); - _commandBuffer.GenerateCompressorEffect( - bufferOffset, - effect.Parameter, - effect.State, - effect.IsEnabled, - nodeId); + _commandBuffer.GenerateCompressorEffect(bufferOffset, + effect.Parameter, + effect.State, + effect.IsEnabled, + nodeId); } private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect) @@ -757,11 +629,8 @@ namespace Ryujinx.Audio.Renderer.Server bool performanceInitialized = false; - if (_performanceManager != null && _performanceManager.GetNextEntry( - out performanceEntry, - effect.GetPerformanceDetailType(), - isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, - nodeId)) + if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(), + isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId)) { performanceInitialized = true; @@ -837,85 +706,6 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateMixWithSplitter( - uint inputBufferIndex, - uint outputBufferIndex, - float volume, - SplitterDestination destination, - ref bool isFirstMixBuffer, - int nodeId) - { - ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); - ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); - - Memory bqfState = _splitterContext.GetBiquadFilterState(destination); - - if (bqf0.Enable && bqf1.Enable) - { - _commandBuffer.GenerateMultiTapBiquadFilterAndMix( - 0f, - volume, - inputBufferIndex, - outputBufferIndex, - 0, - Memory.Empty, - ref bqf0, - ref bqf1, - bqfState[..1], - bqfState.Slice(1, 1), - bqfState.Slice(2, 1), - bqfState.Slice(3, 1), - !destination.IsBiquadFilterEnabledPrev(), - !destination.IsBiquadFilterEnabledPrev(), - false, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(0); - destination.UpdateBiquadFilterEnabledPrev(1); - } - else if (bqf0.Enable) - { - _commandBuffer.GenerateBiquadFilterAndMix( - 0f, - volume, - inputBufferIndex, - outputBufferIndex, - 0, - Memory.Empty, - ref bqf0, - bqfState[..1], - bqfState.Slice(1, 1), - !destination.IsBiquadFilterEnabledPrev(), - false, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(0); - } - else if (bqf1.Enable) - { - _commandBuffer.GenerateBiquadFilterAndMix( - 0f, - volume, - inputBufferIndex, - outputBufferIndex, - 0, - Memory.Empty, - ref bqf1, - bqfState[..1], - bqfState.Slice(1, 1), - !destination.IsBiquadFilterEnabledPrev(), - false, - isFirstMixBuffer, - nodeId); - - destination.UpdateBiquadFilterEnabledPrev(1); - } - - isFirstMixBuffer = false; - } - private void GenerateMix(ref MixState mix) { if (mix.HasAnyDestination()) @@ -932,13 +722,15 @@ namespace Ryujinx.Audio.Renderer.Server { int destinationIndex = destinationId++; - SplitterDestination destination = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex); + Span destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex); - if (destination.IsNull) + if (destinationSpan.IsEmpty) { break; } + ref SplitterDestination destination = ref destinationSpan[0]; + if (destination.IsConfigured()) { int mixId = destination.DestinationId; @@ -949,32 +741,16 @@ namespace Ryujinx.Audio.Renderer.Server uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount); - bool isFirstMixBuffer = true; - for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++) { float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex); if (volume != 0.0f) { - if (destination.IsBiquadFilterEnabled()) - { - GenerateMixWithSplitter( - inputBufferIndex, - destinationMix.BufferOffset + bufferDestinationIndex, - volume, - destination, - ref isFirstMixBuffer, - mix.NodeId); - } - else - { - _commandBuffer.GenerateMix( - inputBufferIndex, - destinationMix.BufferOffset + bufferDestinationIndex, - mix.NodeId, - volume); - } + _commandBuffer.GenerateMix(inputBufferIndex, + destinationMix.BufferOffset + bufferDestinationIndex, + mix.NodeId, + volume); } } } @@ -994,11 +770,10 @@ namespace Ryujinx.Audio.Renderer.Server if (volume != 0.0f) { - _commandBuffer.GenerateMix( - mix.BufferOffset + bufferIndex, - destinationMix.BufferOffset + bufferDestinationIndex, - mix.NodeId, - volume); + _commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex, + destinationMix.BufferOffset + bufferDestinationIndex, + mix.NodeId, + volume); } } } @@ -1008,12 +783,11 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateSubMix(ref MixState subMix) { - _commandBuffer.GenerateDepopForMixBuffers( - _rendererContext.DepopBuffer, - subMix.BufferOffset, - subMix.BufferCount, - subMix.NodeId, - subMix.SampleRate); + _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer, + subMix.BufferOffset, + subMix.BufferCount, + subMix.NodeId, + subMix.SampleRate); GenerateEffects(ref subMix); @@ -1073,12 +847,11 @@ namespace Ryujinx.Audio.Renderer.Server { ref MixState finalMix = ref _mixContext.GetFinalState(); - _commandBuffer.GenerateDepopForMixBuffers( - _rendererContext.DepopBuffer, - finalMix.BufferOffset, - finalMix.BufferCount, - finalMix.NodeId, - finalMix.SampleRate); + _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer, + finalMix.BufferOffset, + finalMix.BufferCount, + finalMix.NodeId, + finalMix.SampleRate); GenerateEffects(ref finalMix); @@ -1109,10 +882,9 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - _commandBuffer.GenerateVolume( - finalMix.Volume, - finalMix.BufferOffset + bufferIndex, - nodeId); + _commandBuffer.GenerateVolume(finalMix.Volume, + finalMix.BufferOffset + bufferIndex, + nodeId); if (performanceSubInitialized) { @@ -1166,45 +938,41 @@ namespace Ryujinx.Audio.Renderer.Server if (useCustomDownMixingCommand) { - _commandBuffer.GenerateDownMixSurroundToStereo( - finalMix.BufferOffset, - sink.Parameter.Input.AsSpan(), - sink.Parameter.Input.AsSpan(), - sink.DownMixCoefficients, - Constants.InvalidNodeId); + _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset, + sink.Parameter.Input.AsSpan(), + sink.Parameter.Input.AsSpan(), + sink.DownMixCoefficients, + Constants.InvalidNodeId); } // NOTE: We do the downmixing at the DSP level as it's easier that way. else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6) { - _commandBuffer.GenerateDownMixSurroundToStereo( - finalMix.BufferOffset, - sink.Parameter.Input.AsSpan(), - sink.Parameter.Input.AsSpan(), - Constants.DefaultSurroundToStereoCoefficients, - Constants.InvalidNodeId); + _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset, + sink.Parameter.Input.AsSpan(), + sink.Parameter.Input.AsSpan(), + Constants.DefaultSurroundToStereoCoefficients, + Constants.InvalidNodeId); } CommandList commandList = _commandBuffer.CommandList; if (sink.UpsamplerState != null) { - _commandBuffer.GenerateUpsample( - finalMix.BufferOffset, - sink.UpsamplerState, - sink.Parameter.InputCount, - sink.Parameter.Input.AsSpan(), - commandList.BufferCount, - commandList.SampleCount, - commandList.SampleRate, - Constants.InvalidNodeId); + _commandBuffer.GenerateUpsample(finalMix.BufferOffset, + sink.UpsamplerState, + sink.Parameter.InputCount, + sink.Parameter.Input.AsSpan(), + commandList.BufferCount, + commandList.SampleCount, + commandList.SampleRate, + Constants.InvalidNodeId); } - _commandBuffer.GenerateDeviceSink( - finalMix.BufferOffset, - sink, - _rendererContext.SessionId, - commandList.Buffers, - Constants.InvalidNodeId); + _commandBuffer.GenerateDeviceSink(finalMix.BufferOffset, + sink, + _rendererContext.SessionId, + commandList.Buffers, + Constants.InvalidNodeId); } private void GenerateSink(BaseSink sink, ref MixState finalMix) diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs index cff754b82..d95e9aa71 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs @@ -170,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server return 0; } - public uint Estimate(MultiTapBiquadFilterCommand command) + public uint Estimate(GroupedBiquadFilterCommand command) { return 0; } @@ -184,15 +184,5 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } - - public uint Estimate(BiquadFilterAndMixCommand command) - { - return 0; - } - - public uint Estimate(MultiTapBiquadFilterAndMixCommand command) - { - return 0; - } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs index ef1326924..929aaf383 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Audio.Renderer.Server return 0; } - public uint Estimate(MultiTapBiquadFilterCommand command) + public uint Estimate(GroupedBiquadFilterCommand command) { return 0; } @@ -476,15 +476,5 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } - - public uint Estimate(BiquadFilterAndMixCommand command) - { - return 0; - } - - public uint Estimate(MultiTapBiquadFilterAndMixCommand command) - { - return 0; - } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs index 31a5347b4..8ae4bc059 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs @@ -632,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server }; } - public virtual uint Estimate(MultiTapBiquadFilterCommand command) + public virtual uint Estimate(GroupedBiquadFilterCommand command) { return 0; } @@ -646,15 +646,5 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } - - public virtual uint Estimate(BiquadFilterAndMixCommand command) - { - return 0; - } - - public virtual uint Estimate(MultiTapBiquadFilterAndMixCommand command) - { - return 0; - } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs index fb357120d..25bc67cd9 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Audio.Renderer.Server { public CommandProcessingTimeEstimatorVersion4(uint sampleCount, uint bufferCount) : base(sampleCount, bufferCount) { } - public override uint Estimate(MultiTapBiquadFilterCommand command) + public override uint Estimate(GroupedBiquadFilterCommand command) { Debug.Assert(SampleCount == 160 || SampleCount == 240); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs index 06f135a88..7135c1c4f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs @@ -210,53 +210,5 @@ namespace Ryujinx.Audio.Renderer.Server _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), }; } - - public override uint Estimate(BiquadFilterAndMixCommand command) - { - Debug.Assert(SampleCount == 160 || SampleCount == 240); - - if (command.HasVolumeRamp) - { - if (SampleCount == 160) - { - return 5204; - } - - return 6683; - } - else - { - if (SampleCount == 160) - { - return 3427; - } - - return 4752; - } - } - - public override uint Estimate(MultiTapBiquadFilterAndMixCommand command) - { - Debug.Assert(SampleCount == 160 || SampleCount == 240); - - if (command.HasVolumeRamp) - { - if (SampleCount == 160) - { - return 7939; - } - - return 10669; - } - else - { - if (SampleCount == 160) - { - return 6256; - } - - return 8683; - } - } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs index 9c4312ad6..27b22363a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs @@ -33,10 +33,8 @@ namespace Ryujinx.Audio.Renderer.Server uint Estimate(UpsampleCommand command); uint Estimate(LimiterCommandVersion1 command); uint Estimate(LimiterCommandVersion2 command); - uint Estimate(MultiTapBiquadFilterCommand command); + uint Estimate(GroupedBiquadFilterCommand command); uint Estimate(CaptureBufferCommand command); uint Estimate(CompressorCommand command); - uint Estimate(BiquadFilterAndMixCommand command); - uint Estimate(MultiTapBiquadFilterAndMixCommand command); } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs index 5ba58ea5b..b90574da9 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs @@ -225,11 +225,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix for (int i = 0; i < splitter.DestinationCount; i++) { - SplitterDestination destination = splitter.GetData(i); + Span destination = splitter.GetData(i); - if (!destination.IsNull) + if (!destination.IsEmpty) { - int destinationMixId = destination.DestinationId; + int destinationMixId = destination[0].DestinationId; if (destinationMixId != UnusedMixId) { diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs index da5a0ad45..0a035916c 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -18,12 +18,16 @@ namespace Ryujinx.Audio.Renderer.Server.Performance if (version == 2) { - return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); } if (version == 1) { - return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); } throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs index 2e5d25b9c..5a70a1bcf 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs @@ -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() + GetEntriesSize() + Unsafe.SizeOf() * _entryDetailIndex); + uint baseEntryOffset = (uint)(Unsafe.SizeOf() + GetEntriesSize() + Unsafe.SizeOf() * _entryDetailIndex); ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex]; diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index a7b82a6bd..3efa783c3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -1,5 +1,4 @@ using Ryujinx.Audio.Renderer.Common; -using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Common; @@ -16,35 +15,15 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public class SplitterContext { - /// - /// Amount of biquad filter states per splitter destination. - /// - public const int BqfStatesPerDestination = 4; - /// /// Storage for . /// private Memory _splitters; /// - /// Storage for . + /// Storage for . /// - private Memory _splitterDestinationsV1; - - /// - /// Storage for . - /// - private Memory _splitterDestinationsV2; - - /// - /// Splitter biquad filtering states. - /// - private Memory _splitterBqfStates; - - /// - /// Version of the splitter context that is being used, currently can be 1 or 2. - /// - public int Version { get; private set; } + private Memory _splitterDestinations; /// /// If set to true, trust the user destination count in . @@ -57,17 +36,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// The behaviour context. /// The audio renderer configuration. /// The . - /// Memory to store the biquad filtering state for splitters during processing. /// Return true if the initialization was successful. - public bool Initialize( - ref BehaviourContext behaviourContext, - ref AudioRendererConfiguration parameter, - WorkBufferAllocator workBufferAllocator, - Memory splitterBqfStates) + public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator) { if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0) { - Setup(Memory.Empty, Memory.Empty, Memory.Empty, false); + Setup(Memory.Empty, Memory.Empty, false); return true; } @@ -86,62 +60,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter splitter = new SplitterState(splitterId++); } - Memory splitterDestinationsV1 = Memory.Empty; - Memory splitterDestinationsV2 = Memory.Empty; + Memory splitterDestinations = workBufferAllocator.Allocate(parameter.SplitterDestinationCount, + SplitterDestination.Alignment); - if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) + if (splitterDestinations.IsEmpty) { - Version = 1; - - splitterDestinationsV1 = workBufferAllocator.Allocate(parameter.SplitterDestinationCount, - SplitterDestinationVersion1.Alignment); - - if (splitterDestinationsV1.IsEmpty) - { - return false; - } - - int splitterDestinationId = 0; - foreach (ref SplitterDestinationVersion1 data in splitterDestinationsV1.Span) - { - data = new SplitterDestinationVersion1(splitterDestinationId++); - } + return false; } - else + + int splitterDestinationId = 0; + foreach (ref SplitterDestination data in splitterDestinations.Span) { - Version = 2; - - splitterDestinationsV2 = workBufferAllocator.Allocate(parameter.SplitterDestinationCount, - SplitterDestinationVersion2.Alignment); - - if (splitterDestinationsV2.IsEmpty) - { - return false; - } - - int splitterDestinationId = 0; - foreach (ref SplitterDestinationVersion2 data in splitterDestinationsV2.Span) - { - data = new SplitterDestinationVersion2(splitterDestinationId++); - } - - if (parameter.SplitterDestinationCount > 0) - { - // Official code stores it in the SplitterDestinationVersion2 struct, - // but we don't to avoid using unsafe code. - - splitterBqfStates.Span.Clear(); - _splitterBqfStates = splitterBqfStates; - } - else - { - _splitterBqfStates = Memory.Empty; - } + data = new SplitterDestination(splitterDestinationId++); } SplitterState.InitializeSplitters(splitters.Span); - Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); + Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed()); return true; } @@ -158,15 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter if (behaviourContext.IsSplitterSupported()) { size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterCount, SplitterState.Alignment); - - if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) - { - size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment); - } - else - { - size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment); - } + size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, SplitterDestination.Alignment); if (behaviourContext.IsSplitterBugFixed()) { @@ -183,18 +110,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Setup the instance. /// /// The storage. - /// The storage. - /// The storage. + /// The storage. /// If set to true, trust the user destination count in . - private void Setup( - Memory splitters, - Memory splitterDestinationsV1, - Memory splitterDestinationsV2, - bool isBugFixed) + private void Setup(Memory splitters, Memory splitterDestinations, bool isBugFixed) { _splitters = splitters; - _splitterDestinationsV1 = splitterDestinationsV1; - _splitterDestinationsV2 = splitterDestinationsV2; + _splitterDestinations = splitterDestinations; IsBugFixed = isBugFixed; } @@ -220,9 +141,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter return 0; } - int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; - - return length / _splitters.Length; + return _splitterDestinations.Length / _splitters.Length; } /// @@ -259,39 +178,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// - /// Update one splitter destination data from user parameters. - /// - /// The raw data after the splitter header. - /// True if the update was successful, false otherwise - private bool UpdateData(ref SequenceReader input) where T : unmanaged, ISplitterDestinationInParameter - { - ref readonly T parameter = ref input.GetRefOrRefToCopy(out _); - - Debug.Assert(parameter.IsMagicValid()); - - if (parameter.IsMagicValid()) - { - int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; - - if (parameter.Id >= 0 && parameter.Id < length) - { - SplitterDestination destination = GetDestination(parameter.Id); - - destination.Update(parameter); - } - - return true; - } - else - { - input.Rewind(Unsafe.SizeOf()); - - return false; - } - } - - /// - /// Update one or multiple splitter destination data from user parameters. + /// Update one or multiple from user parameters. /// /// The splitter header. /// The raw data after the splitter header. @@ -299,23 +186,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { for (int i = 0; i < inputHeader.SplitterDestinationCount; i++) { - if (Version == 1) + ref readonly SplitterDestinationInParameter parameter = ref input.GetRefOrRefToCopy(out _); + + Debug.Assert(parameter.IsMagicValid()); + + if (parameter.IsMagicValid()) { - if (!UpdateData(ref input)) + if (parameter.Id >= 0 && parameter.Id < _splitterDestinations.Length) { - break; - } - } - else if (Version == 2) - { - if (!UpdateData(ref input)) - { - break; + ref SplitterDestination destination = ref GetDestination(parameter.Id); + + destination.Update(parameter); } } else { - Debug.Fail($"Invalid splitter context version {Version}."); + input.Rewind(Unsafe.SizeOf()); + break; } } } @@ -327,7 +214,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Return true if the update was successful. public bool Update(ref SequenceReader input) { - if (!UsingSplitter()) + if (_splitterDestinations.IsEmpty || _splitters.IsEmpty) { return true; } @@ -364,52 +251,45 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// - /// Get a reference to the splitter destination data at the given . + /// Get a reference to a at the given . /// /// The index to use. - /// A reference to the splitter destination data at the given . - public SplitterDestination GetDestination(int id) + /// A reference to a at the given . + public ref SplitterDestination GetDestination(int id) { - if (_splitterDestinationsV2.IsEmpty) - { - return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length)); - } - else - { - return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length)); - } + return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length); } /// - /// Get a in the at and pass to . + /// Get a at the given . + /// + /// The index to use. + /// A at the given . + public Memory GetDestinationMemory(int id) + { + return SpanIOHelper.GetMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length); + } + + /// + /// Get a in the at and pass to . /// /// The index to use to get the . /// The index of the . - /// A . - public SplitterDestination GetDestination(int id, int destinationId) + /// A . + public Span GetDestination(int id, int destinationId) { ref SplitterState splitter = ref GetState(id); return splitter.GetData(destinationId); } - /// - /// Gets the biquad filter state for a given splitter destination. - /// - /// The splitter destination. - /// Biquad filter state for the specified destination. - public Memory GetBiquadFilterState(SplitterDestination destination) - { - return _splitterBqfStates.Slice(destination.Id * BqfStatesPerDestination, BqfStatesPerDestination); - } - /// /// Return true if the audio renderer has any splitters. /// /// True if the audio renderer has any splitters. public bool UsingSplitter() { - return !_splitters.IsEmpty && (!_splitterDestinationsV1.IsEmpty || !_splitterDestinationsV2.IsEmpty); + return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty; } /// diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs index 36dfa5e41..1faf7921f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs @@ -1,198 +1,115 @@ using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Common.Utilities; using System; using System.Diagnostics; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Server.Splitter { /// /// Server state for a splitter destination. /// - public ref struct SplitterDestination + [StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)] + public struct SplitterDestination { - private ref SplitterDestinationVersion1 _v1; - private ref SplitterDestinationVersion2 _v2; + public const int Alignment = 0x10; /// - /// Checks if the splitter destination data reference is null. + /// The unique id of this . /// - public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2); - - /// - /// The splitter unique id. - /// - public int Id - { - get - { - if (Unsafe.IsNullRef(ref _v2)) - { - if (Unsafe.IsNullRef(ref _v1)) - { - return 0; - } - else - { - return _v1.Id; - } - } - else - { - return _v2.Id; - } - } - } + public int Id; /// /// The mix to output the result of the splitter. /// - public int DestinationId - { - get - { - if (Unsafe.IsNullRef(ref _v2)) - { - if (Unsafe.IsNullRef(ref _v1)) - { - return 0; - } - else - { - return _v1.DestinationId; - } - } - else - { - return _v2.DestinationId; - } - } - } + public int DestinationId; + + /// + /// Mix buffer volumes storage. + /// + private MixArray _mix; + private MixArray _previousMix; + + /// + /// Pointer to the next linked element. + /// + private unsafe SplitterDestination* _next; + + /// + /// Set to true if in use. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsUsed; + + /// + /// Set to true if the internal state need to be updated. + /// + [MarshalAs(UnmanagedType.I1)] + public bool NeedToUpdateInternalState; + + [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)] + private struct MixArray { } /// /// Mix buffer volumes. /// /// Used when a splitter id is specified in the mix. - public Span MixBufferVolume - { - get - { - if (Unsafe.IsNullRef(ref _v2)) - { - if (Unsafe.IsNullRef(ref _v1)) - { - return Span.Empty; - } - else - { - return _v1.MixBufferVolume; - } - } - else - { - return _v2.MixBufferVolume; - } - } - } + public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mix); /// /// Previous mix buffer volumes. /// /// Used when a splitter id is specified in the mix. - public Span PreviousMixBufferVolume - { - get - { - if (Unsafe.IsNullRef(ref _v2)) - { - if (Unsafe.IsNullRef(ref _v1)) - { - return Span.Empty; - } - else - { - return _v1.PreviousMixBufferVolume; - } - } - else - { - return _v2.PreviousMixBufferVolume; - } - } - } + public Span PreviousMixBufferVolume => SpanHelpers.AsSpan(ref _previousMix); /// - /// Get the of the next element or null if not present. + /// Get the of the next element or if not present. /// - public readonly SplitterDestination Next + public readonly Span Next { get { unsafe { - if (Unsafe.IsNullRef(ref _v2)) - { - if (Unsafe.IsNullRef(ref _v1)) - { - return new SplitterDestination(); - } - else - { - return new SplitterDestination(ref _v1.Next); - } - } - else - { - return new SplitterDestination(ref _v2.Next); - } + return _next != null ? new Span(_next, 1) : Span.Empty; } } } /// - /// Creates a new splitter destination wrapper for the version 1 splitter destination data. + /// Create a new . /// - /// Version 1 splitter destination data - public SplitterDestination(ref SplitterDestinationVersion1 v1) + /// The unique id of this . + public SplitterDestination(int id) : this() { - _v1 = ref v1; - _v2 = ref Unsafe.NullRef(); + Id = id; + DestinationId = Constants.UnusedMixId; + + ClearVolumes(); } /// - /// Creates a new splitter destination wrapper for the version 2 splitter destination data. - /// - /// Version 2 splitter destination data - public SplitterDestination(ref SplitterDestinationVersion2 v2) - { - - _v1 = ref Unsafe.NullRef(); - _v2 = ref v2; - } - - /// - /// Creates a new splitter destination wrapper for the splitter destination data. - /// - /// Version 1 splitter destination data - /// Version 2 splitter destination data - public unsafe SplitterDestination(SplitterDestinationVersion1* v1, SplitterDestinationVersion2* v2) - { - _v1 = ref Unsafe.AsRef(v1); - _v2 = ref Unsafe.AsRef(v2); - } - - /// - /// Update the splitter destination data from user parameter. + /// Update the from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + public void Update(SplitterDestinationInParameter parameter) { - if (Unsafe.IsNullRef(ref _v2)) + Debug.Assert(Id == parameter.Id); + + if (parameter.IsMagicValid() && Id == parameter.Id) { - _v1.Update(parameter); - } - else - { - _v2.Update(parameter); + DestinationId = parameter.DestinationId; + + parameter.MixBufferVolume.CopyTo(MixBufferVolume); + + if (!IsUsed && parameter.IsUsed) + { + MixBufferVolume.CopyTo(PreviousMixBufferVolume); + + NeedToUpdateInternalState = false; + } + + IsUsed = parameter.IsUsed; } } @@ -201,14 +118,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public void UpdateInternalState() { - if (Unsafe.IsNullRef(ref _v2)) + if (IsUsed && NeedToUpdateInternalState) { - _v1.UpdateInternalState(); - } - else - { - _v2.UpdateInternalState(); + MixBufferVolume.CopyTo(PreviousMixBufferVolume); } + + NeedToUpdateInternalState = false; } /// @@ -216,23 +131,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public void MarkAsNeedToUpdateInternalState() { - if (Unsafe.IsNullRef(ref _v2)) - { - _v1.MarkAsNeedToUpdateInternalState(); - } - else - { - _v2.MarkAsNeedToUpdateInternalState(); - } + NeedToUpdateInternalState = true; } /// - /// Return true if the splitter destination is used and has a destination. + /// Return true if the is used and has a destination. /// - /// True if the splitter destination is used and has a destination. + /// True if the is used and has a destination. public readonly bool IsConfigured() { - return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured(); + return IsUsed && DestinationId != Constants.UnusedMixId; } /// @@ -242,17 +150,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// The volume for the given destination. public float GetMixVolume(int destinationIndex) { - return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex); - } + Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax); - /// - /// Get the previous volume for a given destination. - /// - /// The destination index to use. - /// The volume for the given destination. - public float GetMixVolumePrev(int destinationIndex) - { - return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex); + return MixBufferVolume[destinationIndex]; } /// @@ -260,33 +160,22 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public void ClearVolumes() { - if (Unsafe.IsNullRef(ref _v2)) - { - _v1.ClearVolumes(); - } - else - { - _v2.ClearVolumes(); - } + MixBufferVolume.Clear(); + PreviousMixBufferVolume.Clear(); } /// - /// Link the next element to the given splitter destination. + /// Link the next element to the given . /// - /// The given splitter destination to link. - public void Link(SplitterDestination next) + /// The given to link. + public void Link(ref SplitterDestination next) { - if (Unsafe.IsNullRef(ref _v2)) + unsafe { - Debug.Assert(!Unsafe.IsNullRef(ref next._v1)); - - _v1.Link(ref next._v1); - } - else - { - Debug.Assert(!Unsafe.IsNullRef(ref next._v2)); - - _v2.Link(ref next._v2); + fixed (SplitterDestination* nextPtr = &next) + { + _next = nextPtr; + } } } @@ -295,74 +184,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public void Unlink() { - if (Unsafe.IsNullRef(ref _v2)) + unsafe { - _v1.Unlink(); + _next = null; } - else - { - _v2.Unlink(); - } - } - - /// - /// Checks if any biquad filter is enabled. - /// - /// True if any biquad filter is enabled. - public bool IsBiquadFilterEnabled() - { - return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabled(); - } - - /// - /// Checks if any biquad filter was previously enabled. - /// - /// True if any biquad filter was previously enabled. - public bool IsBiquadFilterEnabledPrev() - { - return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabledPrev(); - } - - /// - /// Gets the biquad filter parameters. - /// - /// Biquad filter index (0 or 1). - /// Biquad filter parameters. - public ref BiquadFilterParameter GetBiquadFilterParameter(int index) - { - Debug.Assert(!Unsafe.IsNullRef(ref _v2)); - - return ref _v2.GetBiquadFilterParameter(index); - } - - /// - /// Checks if any biquad filter was previously enabled. - /// - /// Biquad filter index (0 or 1). - public void UpdateBiquadFilterEnabledPrev(int index) - { - if (!Unsafe.IsNullRef(ref _v2)) - { - _v2.UpdateBiquadFilterEnabledPrev(index); - } - } - - /// - /// Get the reference for the version 1 splitter destination data, or null if version 2 is being used or the destination is null. - /// - /// Reference for the version 1 splitter destination data. - public ref SplitterDestinationVersion1 GetV1RefOrNull() - { - return ref _v1; - } - - /// - /// Get the reference for the version 2 splitter destination data, or null if version 1 is being used or the destination is null. - /// - /// Reference for the version 2 splitter destination data. - public ref SplitterDestinationVersion2 GetV2RefOrNull() - { - return ref _v2; } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs deleted file mode 100644 index 5d2b8fb0f..000000000 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Ryujinx.Audio.Renderer.Parameter; -using Ryujinx.Common.Utilities; -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Audio.Renderer.Server.Splitter -{ - /// - /// Server state for a splitter destination (version 1). - /// - [StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)] - public struct SplitterDestinationVersion1 - { - public const int Alignment = 0x10; - - /// - /// The unique id of this . - /// - public int Id; - - /// - /// The mix to output the result of the splitter. - /// - public int DestinationId; - - /// - /// Mix buffer volumes storage. - /// - private MixArray _mix; - private MixArray _previousMix; - - /// - /// Pointer to the next linked element. - /// - private unsafe SplitterDestinationVersion1* _next; - - /// - /// Set to true if in use. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsUsed; - - /// - /// Set to true if the internal state need to be updated. - /// - [MarshalAs(UnmanagedType.I1)] - public bool NeedToUpdateInternalState; - - [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] - private struct MixArray { } - - /// - /// Mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mix); - - /// - /// Previous mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - public Span PreviousMixBufferVolume => SpanHelpers.AsSpan(ref _previousMix); - - /// - /// Get the reference of the next element or null if not present. - /// - public readonly ref SplitterDestinationVersion1 Next - { - get - { - unsafe - { - return ref Unsafe.AsRef(_next); - } - } - } - - /// - /// Create a new . - /// - /// The unique id of this . - public SplitterDestinationVersion1(int id) : this() - { - Id = id; - DestinationId = Constants.UnusedMixId; - - ClearVolumes(); - } - - /// - /// Update the from user parameter. - /// - /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter - { - Debug.Assert(Id == parameter.Id); - - if (parameter.IsMagicValid() && Id == parameter.Id) - { - DestinationId = parameter.DestinationId; - - parameter.MixBufferVolume.CopyTo(MixBufferVolume); - - if (!IsUsed && parameter.IsUsed) - { - MixBufferVolume.CopyTo(PreviousMixBufferVolume); - - NeedToUpdateInternalState = false; - } - - IsUsed = parameter.IsUsed; - } - } - - /// - /// Update the internal state of the instance. - /// - public void UpdateInternalState() - { - if (IsUsed && NeedToUpdateInternalState) - { - MixBufferVolume.CopyTo(PreviousMixBufferVolume); - } - - NeedToUpdateInternalState = false; - } - - /// - /// Set the update internal state marker. - /// - public void MarkAsNeedToUpdateInternalState() - { - NeedToUpdateInternalState = true; - } - - /// - /// Return true if the is used and has a destination. - /// - /// True if the is used and has a destination. - public readonly bool IsConfigured() - { - return IsUsed && DestinationId != Constants.UnusedMixId; - } - - /// - /// Get the volume for a given destination. - /// - /// The destination index to use. - /// The volume for the given destination. - public float GetMixVolume(int destinationIndex) - { - Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax); - - return MixBufferVolume[destinationIndex]; - } - - /// - /// Get the previous volume for a given destination. - /// - /// The destination index to use. - /// The volume for the given destination. - public float GetMixVolumePrev(int destinationIndex) - { - Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax); - - return PreviousMixBufferVolume[destinationIndex]; - } - - /// - /// Clear the volumes. - /// - public void ClearVolumes() - { - MixBufferVolume.Clear(); - PreviousMixBufferVolume.Clear(); - } - - /// - /// Link the next element to the given . - /// - /// The given to link. - public void Link(ref SplitterDestinationVersion1 next) - { - unsafe - { - fixed (SplitterDestinationVersion1* nextPtr = &next) - { - _next = nextPtr; - } - } - } - - /// - /// Remove the link to the next element. - /// - public void Unlink() - { - unsafe - { - _next = null; - } - } - } -} diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs deleted file mode 100644 index f9487909d..000000000 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs +++ /dev/null @@ -1,250 +0,0 @@ -using Ryujinx.Audio.Renderer.Parameter; -using Ryujinx.Common.Memory; -using Ryujinx.Common.Utilities; -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Audio.Renderer.Server.Splitter -{ - /// - /// Server state for a splitter destination (version 2). - /// - [StructLayout(LayoutKind.Sequential, Size = 0x110, Pack = Alignment)] - public struct SplitterDestinationVersion2 - { - public const int Alignment = 0x10; - - /// - /// The unique id of this . - /// - public int Id; - - /// - /// The mix to output the result of the splitter. - /// - public int DestinationId; - - /// - /// Mix buffer volumes storage. - /// - private MixArray _mix; - private MixArray _previousMix; - - /// - /// Pointer to the next linked element. - /// - private unsafe SplitterDestinationVersion2* _next; - - /// - /// Set to true if in use. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsUsed; - - /// - /// Set to true if the internal state need to be updated. - /// - [MarshalAs(UnmanagedType.I1)] - public bool NeedToUpdateInternalState; - - [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] - private struct MixArray { } - - /// - /// Mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mix); - - /// - /// Previous mix buffer volumes. - /// - /// Used when a splitter id is specified in the mix. - public Span PreviousMixBufferVolume => SpanHelpers.AsSpan(ref _previousMix); - - /// - /// Get the reference of the next element or null if not present. - /// - public readonly ref SplitterDestinationVersion2 Next - { - get - { - unsafe - { - return ref Unsafe.AsRef(_next); - } - } - } - - private Array2 _biquadFilters; - - private Array2 _isPreviousBiquadFilterEnabled; - - /// - /// Create a new . - /// - /// The unique id of this . - public SplitterDestinationVersion2(int id) : this() - { - Id = id; - DestinationId = Constants.UnusedMixId; - - ClearVolumes(); - } - - /// - /// Update the from user parameter. - /// - /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter - { - Debug.Assert(Id == parameter.Id); - - if (parameter.IsMagicValid() && Id == parameter.Id) - { - DestinationId = parameter.DestinationId; - - parameter.MixBufferVolume.CopyTo(MixBufferVolume); - - _biquadFilters = parameter.BiquadFilters; - - if (!IsUsed && parameter.IsUsed) - { - MixBufferVolume.CopyTo(PreviousMixBufferVolume); - - NeedToUpdateInternalState = false; - } - - IsUsed = parameter.IsUsed; - } - } - - /// - /// Update the internal state of the instance. - /// - public void UpdateInternalState() - { - if (IsUsed && NeedToUpdateInternalState) - { - MixBufferVolume.CopyTo(PreviousMixBufferVolume); - } - - NeedToUpdateInternalState = false; - } - - /// - /// Set the update internal state marker. - /// - public void MarkAsNeedToUpdateInternalState() - { - NeedToUpdateInternalState = true; - } - - /// - /// Return true if the is used and has a destination. - /// - /// True if the is used and has a destination. - public readonly bool IsConfigured() - { - return IsUsed && DestinationId != Constants.UnusedMixId; - } - - /// - /// Get the volume for a given destination. - /// - /// The destination index to use. - /// The volume for the given destination. - public float GetMixVolume(int destinationIndex) - { - Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax); - - return MixBufferVolume[destinationIndex]; - } - - /// - /// Get the previous volume for a given destination. - /// - /// The destination index to use. - /// The volume for the given destination. - public float GetMixVolumePrev(int destinationIndex) - { - Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax); - - return PreviousMixBufferVolume[destinationIndex]; - } - - /// - /// Clear the volumes. - /// - public void ClearVolumes() - { - MixBufferVolume.Clear(); - PreviousMixBufferVolume.Clear(); - } - - /// - /// Link the next element to the given . - /// - /// The given to link. - public void Link(ref SplitterDestinationVersion2 next) - { - unsafe - { - fixed (SplitterDestinationVersion2* nextPtr = &next) - { - _next = nextPtr; - } - } - } - - /// - /// Remove the link to the next element. - /// - public void Unlink() - { - unsafe - { - _next = null; - } - } - - /// - /// Checks if any biquad filter is enabled. - /// - /// True if any biquad filter is enabled. - public bool IsBiquadFilterEnabled() - { - return _biquadFilters[0].Enable || _biquadFilters[1].Enable; - } - - /// - /// Checks if any biquad filter was previously enabled. - /// - /// True if any biquad filter was previously enabled. - public bool IsBiquadFilterEnabledPrev() - { - return _isPreviousBiquadFilterEnabled[0]; - } - - /// - /// Gets the biquad filter parameters. - /// - /// Biquad filter index (0 or 1). - /// Biquad filter parameters. - public ref BiquadFilterParameter GetBiquadFilterParameter(int index) - { - return ref _biquadFilters[index]; - } - - /// - /// Checks if any biquad filter was previously enabled. - /// - /// Biquad filter index (0 or 1). - public void UpdateBiquadFilterEnabledPrev(int index) - { - _isPreviousBiquadFilterEnabled[index] = _biquadFilters[index].Enable; - } - } -} diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs index 3e7dce559..944f092d2 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs @@ -15,8 +15,6 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { public const int Alignment = 0x10; - private delegate void SplitterDestinationAction(SplitterDestination destination, int index); - /// /// The unique id of this . /// @@ -28,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter public uint SampleRate; /// - /// Count of splitter destinations. + /// Count of splitter destinations (). /// public int DestinationCount; @@ -39,25 +37,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter public bool HasNewConnection; /// - /// Linked list of . + /// Linked list of . /// - private unsafe SplitterDestinationVersion1* _destinationDataV1; + private unsafe SplitterDestination* _destinationsData; /// - /// Linked list of . + /// Span to the first element of the linked list of . /// - private unsafe SplitterDestinationVersion2* _destinationDataV2; - - /// - /// First element of the linked list of splitter destinations data. - /// - public readonly SplitterDestination Destination + public readonly Span Destinations { get { unsafe { - return new SplitterDestination(_destinationDataV1, _destinationDataV2); + return (IntPtr)_destinationsData != IntPtr.Zero ? new Span(_destinationsData, 1) : Span.Empty; } } } @@ -71,20 +64,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter Id = id; } - public readonly SplitterDestination GetData(int index) + public readonly Span GetData(int index) { int i = 0; - SplitterDestination result = Destination; + Span result = Destinations; while (i < index) { - if (result.IsNull) + if (result.IsEmpty) { break; } - result = result.Next; + result = result[0].Next; i++; } @@ -100,25 +93,25 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// - /// Utility function to apply an action to all . + /// Utility function to apply a given to all . /// /// The action to execute on each elements. - private readonly void ForEachDestination(SplitterDestinationAction action) + private readonly void ForEachDestination(SpanAction action) { - SplitterDestination temp = Destination; + Span temp = Destinations; int i = 0; while (true) { - if (temp.IsNull) + if (temp.IsEmpty) { break; } - SplitterDestination next = temp.Next; + Span next = temp[0].Next; - action(temp, i++); + action.Invoke(temp, i++); temp = next; } @@ -149,9 +142,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { input.ReadLittleEndian(out int destinationId); - SplitterDestination destination = context.GetDestination(destinationId); + Memory destination = context.GetDestinationMemory(destinationId); - SetDestination(destination); + SetDestination(ref destination.Span[0]); DestinationCount = destinationCount; @@ -159,9 +152,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { input.ReadLittleEndian(out destinationId); - SplitterDestination nextDestination = context.GetDestination(destinationId); + Memory nextDestination = context.GetDestinationMemory(destinationId); - destination.Link(nextDestination); + destination.Span[0].Link(ref nextDestination.Span[0]); destination = nextDestination; } } @@ -181,21 +174,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// - /// Set the head of the linked list of . + /// Set the head of the linked list of . /// - /// New destination value. - public void SetDestination(SplitterDestination newValue) + /// A reference to a . + public void SetDestination(ref SplitterDestination newValue) { unsafe { - fixed (SplitterDestinationVersion1* newValuePtr = &newValue.GetV1RefOrNull()) + fixed (SplitterDestination* newValuePtr = &newValue) { - _destinationDataV1 = newValuePtr; - } - - fixed (SplitterDestinationVersion2* newValuePtr = &newValue.GetV2RefOrNull()) - { - _destinationDataV2 = newValuePtr; + _destinationsData = newValuePtr; } } } @@ -205,20 +193,19 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public readonly void UpdateInternalState() { - ForEachDestination((destination, _) => destination.UpdateInternalState()); + ForEachDestination((destination, _) => destination[0].UpdateInternalState()); } /// - /// Clear all links from the . + /// Clear all links from the . /// public void ClearLinks() { - ForEachDestination((destination, _) => destination.Unlink()); + ForEachDestination((destination, _) => destination[0].Unlink()); unsafe { - _destinationDataV1 = null; - _destinationDataV2 = null; + _destinationsData = (SplitterDestination*)IntPtr.Zero; } } @@ -232,8 +219,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { unsafe { - splitter._destinationDataV1 = null; - splitter._destinationDataV2 = null; + splitter._destinationsData = (SplitterDestination*)IntPtr.Zero; } splitter.DestinationCount = 0; diff --git a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs index a9163f348..7fe2a4f02 100644 --- a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs +++ b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs @@ -1,33 +1,13 @@ -using Ryujinx.Common.Utilities; using System; namespace Ryujinx.Common.GraphicsDriver { public static class DriverUtilities { - private static void AddMesaFlags(string envVar, string newFlags) - { - string existingFlags = Environment.GetEnvironmentVariable(envVar); - - string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}"; - - OsUtils.SetEnvironmentVariableNoCaching(envVar, flags); - } - - public static void InitDriverConfig(bool oglThreading) - { - if (OperatingSystem.IsLinux()) - { - AddMesaFlags("RADV_DEBUG", "nodcc"); - } - - ToggleOGLThreading(oglThreading); - } - public static void ToggleOGLThreading(bool enabled) { - OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower()); - OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); + Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower()); + Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); try { diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs new file mode 100644 index 000000000..05fb29ac7 --- /dev/null +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs @@ -0,0 +1,51 @@ +using System; +using System.Buffers; +using System.Threading; + +namespace Ryujinx.Common.Memory +{ + public partial class ByteMemoryPool + { + /// + /// Represents a that wraps an array rented from + /// and exposes it as + /// with a length of the requested size. + /// + private sealed class ByteMemoryPoolBuffer : IMemoryOwner + { + private byte[] _array; + private readonly int _length; + + public ByteMemoryPoolBuffer(int length) + { + _array = ArrayPool.Shared.Rent(length); + _length = length; + } + + /// + /// Returns a belonging to this owner. + /// + public Memory Memory + { + get + { + byte[] array = _array; + + ObjectDisposedException.ThrowIf(array is null, this); + + return new Memory(array, 0, _length); + } + } + + public void Dispose() + { + var array = Interlocked.Exchange(ref _array, null); + + if (array != null) + { + ArrayPool.Shared.Return(array); + } + } + } + } +} diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs new file mode 100644 index 000000000..6fd6a98aa --- /dev/null +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs @@ -0,0 +1,106 @@ +using System; +using System.Buffers; + +namespace Ryujinx.Common.Memory +{ + /// + /// Provides a pool of re-usable byte array instances. + /// + public static partial class ByteMemoryPool + { + /// + /// Returns the maximum buffer size supported by this pool. + /// + public static int MaxBufferSize => Array.MaxLength; + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner Rent(long length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner Rent(ulong length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner Rent(int length) + => RentImpl(length); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner RentCleared(long length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner RentCleared(ulong length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public static IMemoryOwner RentCleared(int length) + { + var buffer = RentImpl(length); + + buffer.Memory.Span.Clear(); + + return buffer; + } + + /// + /// Copies into a newly rented byte memory buffer. + /// + /// The byte buffer to copy + /// A wrapping the rented memory with copied to it + public static IMemoryOwner RentCopy(ReadOnlySpan 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); + } + } +} diff --git a/src/Ryujinx.Common/Memory/MemoryOwner.cs b/src/Ryujinx.Common/Memory/MemoryOwner.cs deleted file mode 100644 index b7fe1db77..000000000 --- a/src/Ryujinx.Common/Memory/MemoryOwner.cs +++ /dev/null @@ -1,140 +0,0 @@ -#nullable enable -using System; -using System.Buffers; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Ryujinx.Common.Memory -{ - /// - /// An implementation with an embedded length and fast - /// accessor, with memory allocated from . - /// - /// The type of item to store. - public sealed class MemoryOwner : IMemoryOwner - { - private readonly int _length; - private T[]? _array; - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The length of the new memory buffer to use - private MemoryOwner(int length) - { - _length = length; - _array = ArrayPool.Shared.Rent(length); - } - - /// - /// Creates a new instance with the specified length. - /// - /// The length of the new memory buffer to use - /// A instance of the requested length - /// Thrown when is not valid - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Rent(int length) => new(length); - - /// - /// Creates a new instance with the specified length and the content cleared. - /// - /// The length of the new memory buffer to use - /// A instance of the requested length and the content cleared - /// Thrown when is not valid - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner RentCleared(int length) - { - MemoryOwner result = new(length); - - result._array.AsSpan(0, length).Clear(); - - return result; - } - - /// - /// Creates a new instance with the content copied from the specified buffer. - /// - /// The buffer to copy - /// A instance with the same length and content as - public static MemoryOwner RentCopy(ReadOnlySpan buffer) - { - MemoryOwner result = new(buffer.Length); - - buffer.CopyTo(result._array); - - return result; - } - - /// - /// Gets the number of items in the current instance. - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _length; - } - - /// - public Memory Memory - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = _array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return new(array, 0, _length); - } - } - - /// - /// Gets a wrapping the memory belonging to the current instance. - /// - /// - /// Uses a trick made possible by the .NET 6+ runtime array layout. - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = _array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array); - - return MemoryMarshal.CreateSpan(ref firstElementRef, _length); - } - } - - /// - public void Dispose() - { - T[]? array = Interlocked.Exchange(ref _array, null); - - if (array is not null) - { - ArrayPool.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences()); - } - } - - /// - /// Throws an when is . - /// - [DoesNotReturn] - private static void ThrowObjectDisposedException() - { - throw new ObjectDisposedException(nameof(MemoryOwner), "The buffer has already been disposed."); - } - } -} diff --git a/src/Ryujinx.Common/Memory/SpanOwner.cs b/src/Ryujinx.Common/Memory/SpanOwner.cs deleted file mode 100644 index acb20bcad..000000000 --- a/src/Ryujinx.Common/Memory/SpanOwner.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Common.Memory -{ - /// - /// A stack-only type that rents a buffer of a specified length from . - /// It does not implement to avoid being boxed, but should still be disposed. This - /// is easy since C# 8, which allows use of C# `using` constructs on any type that has a public Dispose() method. - /// To keep this type simple, fast, and read-only, it does not check or guard against multiple disposals. - /// For all these reasons, all usage should be with a `using` block or statement. - /// - /// The type of item to store. - public readonly ref struct SpanOwner - { - private readonly int _length; - private readonly T[] _array; - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The length of the new memory buffer to use - private SpanOwner(int length) - { - _length = length; - _array = ArrayPool.Shared.Rent(length); - } - - /// - /// Gets an empty instance. - /// - public static SpanOwner Empty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(0); - } - - /// - /// Creates a new instance with the specified length. - /// - /// The length of the new memory buffer to use - /// A instance of the requested length - /// Thrown when is not valid - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner Rent(int length) => new(length); - - /// - /// Creates a new instance with the length and the content cleared. - /// - /// The length of the new memory buffer to use - /// A instance of the requested length and the content cleared - /// Thrown when is not valid - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner RentCleared(int length) - { - SpanOwner result = new(length); - - result._array.AsSpan(0, length).Clear(); - - return result; - } - - /// - /// Creates a new instance with the content copied from the specified buffer. - /// - /// The buffer to copy - /// A instance with the same length and content as - public static SpanOwner RentCopy(ReadOnlySpan buffer) - { - SpanOwner result = new(buffer.Length); - - buffer.CopyTo(result._array); - - return result; - } - - /// - /// Gets the number of items in the current instance - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _length; - } - - /// - /// Gets a wrapping the memory belonging to the current instance. - /// - /// - /// Uses a trick made possible by the .NET 6+ runtime array layout. - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(_array); - - return MemoryMarshal.CreateSpan(ref firstElementRef, _length); - } - } - - /// - /// Implements the duck-typed method. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - ArrayPool.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences()); - } - } -} diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs index 7530c012a..e22571c96 100644 --- a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -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 MemoryOwner ReadFileToRentedMemory(string filename) + public static IMemoryOwner ReadFileToRentedMemory(string filename) { var (assembly, path) = ResolveManifestPath(filename); return ReadFileToRentedMemory(assembly, path); } - public static MemoryOwner ReadFileToRentedMemory(Assembly assembly, string filename) + public static IMemoryOwner ReadFileToRentedMemory(Assembly assembly, string filename) { using var stream = GetStream(assembly, filename); diff --git a/src/Ryujinx.Common/Utilities/OsUtils.cs b/src/Ryujinx.Common/Utilities/OsUtils.cs deleted file mode 100644 index a0791b092..000000000 --- a/src/Ryujinx.Common/Utilities/OsUtils.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Ryujinx.Common.Utilities -{ - public partial class OsUtils - { - [LibraryImport("libc", SetLastError = true)] - private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); - - public static void SetEnvironmentVariableNoCaching(string key, string value) - { - // Set the value in the cached environment variables, too. - Environment.SetEnvironmentVariable(key, value); - - if (!OperatingSystem.IsWindows()) - { - int res = setenv(key, value, 1); - Debug.Assert(res != -1); - } - } - } -} diff --git a/src/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs index aeb6e0d52..74b6af5ec 100644 --- a/src/Ryujinx.Common/Utilities/StreamUtils.cs +++ b/src/Ryujinx.Common/Utilities/StreamUtils.cs @@ -1,5 +1,6 @@ using Microsoft.IO; using Ryujinx.Common.Memory; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -15,7 +16,7 @@ namespace Ryujinx.Common.Utilities return output.ToArray(); } - public static MemoryOwner StreamToRentedMemory(Stream input) + public static IMemoryOwner StreamToRentedMemory(Stream input) { if (input is MemoryStream inputMemoryStream) { @@ -25,9 +26,9 @@ namespace Ryujinx.Common.Utilities { long bytesExpected = input.Length; - MemoryOwner ownedMemory = MemoryOwner.Rent(checked((int)bytesExpected)); + IMemoryOwner ownedMemory = ByteMemoryPool.Rent(bytesExpected); - var destSpan = ownedMemory.Span; + var destSpan = ownedMemory.Memory.Span; int totalBytesRead = 0; @@ -65,14 +66,14 @@ namespace Ryujinx.Common.Utilities return stream.ToArray(); } - private static MemoryOwner MemoryStreamToRentedMemory(MemoryStream input) + private static IMemoryOwner MemoryStreamToRentedMemory(MemoryStream input) { input.Position = 0; - MemoryOwner ownedMemory = MemoryOwner.Rent(checked((int)input.Length)); + IMemoryOwner ownedMemory = ByteMemoryPool.Rent(input.Length); // Discard the return value because we assume reading a MemoryStream always succeeds completely. - _ = input.Read(ownedMemory.Span); + _ = input.Read(ownedMemory.Memory.Span); return ownedMemory; } diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs index 501109b86..663d0aeb1 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs @@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(size); + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size); - Read(va, memoryOwner.Span); + Read(va, memoryOwner.Memory.Span); return new WritableRegion(this, va, memoryOwner); } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs index d57750fc1..88850cb33 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs @@ -1,5 +1,6 @@ using Ryujinx.Cpu.LightningJit.CodeGen; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System.Diagnostics; namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 { diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs index f1b6e395b..e2354f448 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs @@ -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); - EmitSaturateUqadd(context, d, 16); + EmitSaturateUnsignedRange(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); - EmitSaturateUqadd(context, d, 8); + EmitSaturateUnsignedRange(context, d, 8); }); } @@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 context.Arm64Assembler.Add(d, n, m); } - EmitSaturateUq(context, d, 16, e == 0); + EmitSaturateUnsignedRange(context, d, 16); }); } @@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 context.Arm64Assembler.Sub(d, n, m); } - EmitSaturateUq(context, d, 16, e != 0); + EmitSaturateUnsignedRange(context, d, 16); }); } public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm) { - InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) => + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) => { context.Arm64Assembler.Sub(d, n, m); - EmitSaturateUqsub(context, d, 16); + EmitSaturateUnsignedRange(context, d, 16); }); } public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm) { - InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) => + InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) => { context.Arm64Assembler.Sub(d, n, m); - EmitSaturateUqsub(context, d, 8); + EmitSaturateUnsignedRange(context, d, 8); }); } @@ -358,17 +358,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 } } - 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) + private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo) { Debug.Assert(saturateTo <= 32); @@ -389,7 +379,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 return; } - context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo)); + context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo)); int branchIndex = context.CodeWriter.InstructionPointer; @@ -397,7 +387,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 context.Arm64Assembler.Cbz(tempRegister.Operand, 0); // Saturate. - context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo)); + context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo)); int delta = context.CodeWriter.InstructionPointer - branchIndex; context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); diff --git a/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs b/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs index cb1a7c3ab..fc075a264 100644 --- a/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs +++ b/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Memory; using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -144,9 +145,9 @@ namespace Ryujinx.Graphics.Device } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(size); + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size); - ReadImpl(va, memoryOwner.Span); + GetSpan(va, size).CopyTo(memoryOwner.Memory.Span); return new WritableRegion(this, va, memoryOwner, tracked: true); } diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs index 54178a414..de8582a3b 100644 --- a/src/Ryujinx.Graphics.Device/DeviceState.cs +++ b/src/Ryujinx.Graphics.Device/DeviceState.cs @@ -39,10 +39,7 @@ namespace Ryujinx.Graphics.Device { var field = fields[fieldIndex]; - var currentFieldOffset = (int)Marshal.OffsetOf(field.Name); - var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name); - - int sizeOfField = nextFieldOffset - currentFieldOffset; + int sizeOfField = SizeCalculator.SizeOf(field.FieldType); for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4) { diff --git a/src/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs new file mode 100644 index 000000000..54820ec36 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/SizeCalculator.cs @@ -0,0 +1,63 @@ +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."), + }; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/BufferAccess.cs b/src/Ryujinx.Graphics.GAL/BufferAccess.cs index 1e7736f8f..faefa5188 100644 --- a/src/Ryujinx.Graphics.GAL/BufferAccess.cs +++ b/src/Ryujinx.Graphics.GAL/BufferAccess.cs @@ -6,13 +6,8 @@ namespace Ryujinx.Graphics.GAL public enum BufferAccess { Default = 0, - HostMemory = 1, - DeviceMemory = 2, - DeviceMemoryMapped = 3, - - MemoryTypeMask = 0xf, - - Stream = 1 << 4, - SparseCompatible = 1 << 5, + FlushPersistent = 1 << 0, + Stream = 1 << 1, + SparseCompatible = 1 << 2, } } diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index 1eec80e51..779ce5b5d 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.GAL { public readonly TargetApi Api; public readonly string VendorName; - public readonly SystemMemoryType MemoryType; public readonly bool HasFrontFacingBug; public readonly bool HasVectorIndexingBug; @@ -51,13 +50,6 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsIndirectParameters; public readonly bool SupportsDepthClipControl; - public readonly int UniformBufferSetIndex; - public readonly int StorageBufferSetIndex; - public readonly int TextureSetIndex; - public readonly int ImageSetIndex; - public readonly int ExtraSetBaseIndex; - public readonly int MaximumExtraSets; - public readonly uint MaximumUniformBuffersPerStage; public readonly uint MaximumStorageBuffersPerStage; public readonly uint MaximumTexturesPerStage; @@ -71,12 +63,9 @@ namespace Ryujinx.Graphics.GAL public readonly int GatherBiasPrecision; - public readonly ulong MaximumGpuMemory; - public Capabilities( TargetApi api, string vendorName, - SystemMemoryType memoryType, bool hasFrontFacingBug, bool hasVectorIndexingBug, bool needsFragmentOutputSpecialization, @@ -118,12 +107,6 @@ namespace Ryujinx.Graphics.GAL bool supportsViewportSwizzle, bool supportsIndirectParameters, bool supportsDepthClipControl, - int uniformBufferSetIndex, - int storageBufferSetIndex, - int textureSetIndex, - int imageSetIndex, - int extraSetBaseIndex, - int maximumExtraSets, uint maximumUniformBuffersPerStage, uint maximumStorageBuffersPerStage, uint maximumTexturesPerStage, @@ -133,12 +116,10 @@ namespace Ryujinx.Graphics.GAL int shaderSubgroupSize, int storageBufferOffsetAlignment, int textureBufferOffsetAlignment, - int gatherBiasPrecision, - ulong maximumGpuMemory) + int gatherBiasPrecision) { Api = api; VendorName = vendorName; - MemoryType = memoryType; HasFrontFacingBug = hasFrontFacingBug; HasVectorIndexingBug = hasVectorIndexingBug; NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization; @@ -180,12 +161,6 @@ namespace Ryujinx.Graphics.GAL SupportsViewportSwizzle = supportsViewportSwizzle; SupportsIndirectParameters = supportsIndirectParameters; SupportsDepthClipControl = supportsDepthClipControl; - UniformBufferSetIndex = uniformBufferSetIndex; - StorageBufferSetIndex = storageBufferSetIndex; - TextureSetIndex = textureSetIndex; - ImageSetIndex = imageSetIndex; - ExtraSetBaseIndex = extraSetBaseIndex; - MaximumExtraSets = maximumExtraSets; MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage; MaximumTexturesPerStage = maximumTexturesPerStage; @@ -196,7 +171,6 @@ namespace Ryujinx.Graphics.GAL StorageBufferOffsetAlignment = storageBufferOffsetAlignment; TextureBufferOffsetAlignment = textureBufferOffsetAlignment; GatherBiasPrecision = gatherBiasPrecision; - MaximumGpuMemory = maximumGpuMemory; } } } diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 17c42d2d4..bd3b38a9a 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -711,36 +711,5 @@ namespace Ryujinx.Graphics.GAL { return format.IsUint() || format.IsSint(); } - - /// - /// Checks if the texture format is a float or sRGB color format. - /// - /// - /// Does not include normalized, compressed or depth formats. - /// Float and sRGB formats do not participate in logical operations. - /// - /// Texture format - /// True if the format is a float or sRGB color format, false otherwise - public static bool IsFloatOrSrgb(this Format format) - { - switch (format) - { - case Format.R8G8B8A8Srgb: - case Format.B8G8R8A8Srgb: - case Format.R16Float: - case Format.R16G16Float: - case Format.R16G16B16Float: - case Format.R16G16B16A16Float: - case Format.R32Float: - case Format.R32G32Float: - case Format.R32G32B32Float: - case Format.R32G32B32A32Float: - case Format.R11G11B10Float: - case Format.R9G9B9E5Float: - return true; - } - - return false; - } } } diff --git a/src/Ryujinx.Graphics.GAL/IImageArray.cs b/src/Ryujinx.Graphics.GAL/IImageArray.cs index d87314eb8..30cff50b1 100644 --- a/src/Ryujinx.Graphics.GAL/IImageArray.cs +++ b/src/Ryujinx.Graphics.GAL/IImageArray.cs @@ -1,9 +1,8 @@ -using System; - namespace Ryujinx.Graphics.GAL { - public interface IImageArray : IDisposable + public interface IImageArray { + void SetFormats(int index, Format[] imageFormats); void SetImages(int index, ITexture[] images); } } diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index b8409a573..9efb9e3e8 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -58,9 +58,8 @@ namespace Ryujinx.Graphics.GAL void SetIndexBuffer(BufferRange buffer, IndexType type); - void SetImage(ShaderStage stage, int binding, ITexture texture); + void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat); void SetImageArray(ShaderStage stage, int binding, IImageArray array); - void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array); void SetLineParameters(float width, bool smooth); @@ -92,7 +91,6 @@ namespace Ryujinx.Graphics.GAL void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler); void SetTextureArray(ShaderStage stage, int binding, ITextureArray array); - void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array); void SetTransformFeedbackBuffers(ReadOnlySpan buffers); void SetUniformBuffers(ReadOnlySpan buffers); diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index 85d0bd729..a3466e396 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.GAL void BackgroundContextAction(Action action, bool alwaysBackground = false); BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default); + BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint); BufferHandle CreateBuffer(nint pointer, int size); BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers); diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index 2aa4053ff..2d9c6b799 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using System.Buffers; namespace Ryujinx.Graphics.GAL { @@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL PinnedSpan GetData(int layer, int level); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes - void SetData(MemoryOwner data); + void SetData(IMemoryOwner data); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes /// Target layer /// Target level - void SetData(MemoryOwner data, int layer, int level); + void SetData(IMemoryOwner data, int layer, int level); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes /// Target layer /// Target level /// Target sub-region of the texture to update - void SetData(MemoryOwner data, int layer, int level, Rectangle region); + void SetData(IMemoryOwner data, int layer, int level, Rectangle region); void SetStorage(BufferRange buffer); diff --git a/src/Ryujinx.Graphics.GAL/ITextureArray.cs b/src/Ryujinx.Graphics.GAL/ITextureArray.cs index 9ee79dacb..35c2116b5 100644 --- a/src/Ryujinx.Graphics.GAL/ITextureArray.cs +++ b/src/Ryujinx.Graphics.GAL/ITextureArray.cs @@ -1,8 +1,6 @@ -using System; - namespace Ryujinx.Graphics.GAL { - public interface ITextureArray : IDisposable + public interface ITextureArray { void SetSamplers(int index, ISampler[] samplers); void SetTextures(int index, ITexture[] textures); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index a1e6db971..fd2919be4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading } Register(CommandType.Action); + Register(CommandType.CreateBuffer); Register(CommandType.CreateBufferAccess); Register(CommandType.CreateBufferSparse); Register(CommandType.CreateHostBuffer); @@ -66,7 +67,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register(CommandType.CounterEventDispose); Register(CommandType.CounterEventFlush); - Register(CommandType.ImageArrayDispose); + Register(CommandType.ImageArraySetFormats); Register(CommandType.ImageArraySetImages); Register(CommandType.ProgramDispose); @@ -88,7 +89,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register(CommandType.TextureSetDataSliceRegion); Register(CommandType.TextureSetStorage); - Register(CommandType.TextureArrayDispose); Register(CommandType.TextureArraySetSamplers); Register(CommandType.TextureArraySetTextures); @@ -125,7 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register(CommandType.SetUniformBuffers); Register(CommandType.SetImage); Register(CommandType.SetImageArray); - Register(CommandType.SetImageArraySeparate); Register(CommandType.SetIndexBuffer); Register(CommandType.SetLineParameters); Register(CommandType.SetLogicOpState); @@ -143,7 +142,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register(CommandType.SetStencilTest); Register(CommandType.SetTextureAndSampler); Register(CommandType.SetTextureArray); - Register(CommandType.SetTextureArraySeparate); Register(CommandType.SetUserClipDistance); Register(CommandType.SetVertexAttribs); Register(CommandType.SetVertexBuffers); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index 348c8e462..a5e7336cd 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading enum CommandType : byte { Action, + CreateBuffer, CreateBufferAccess, CreateBufferSparse, CreateHostBuffer, @@ -26,7 +27,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading CounterEventDispose, CounterEventFlush, - ImageArrayDispose, + ImageArraySetFormats, ImageArraySetImages, ProgramDispose, @@ -48,7 +49,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading TextureSetDataSliceRegion, TextureSetStorage, - TextureArrayDispose, TextureArraySetSamplers, TextureArraySetTextures, @@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading SetUniformBuffers, SetImage, SetImageArray, - SetImageArraySeparate, SetIndexBuffer, SetLineParameters, SetLogicOpState, @@ -103,7 +102,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading SetStencilTest, SetTextureAndSampler, SetTextureArray, - SetTextureArraySeparate, SetUserClipDistance, SetVertexAttribs, SetVertexBuffers, diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArrayDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArrayDisposeCommand.cs deleted file mode 100644 index ac2ac933b..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArrayDisposeCommand.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Ryujinx.Graphics.GAL.Multithreading.Model; -using Ryujinx.Graphics.GAL.Multithreading.Resources; - -namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray -{ - struct ImageArrayDisposeCommand : IGALCommand, IGALCommand - { - public readonly CommandType CommandType => CommandType.ImageArrayDispose; - private TableRef _imageArray; - - public void Set(TableRef imageArray) - { - _imageArray = imageArray; - } - - public static void Run(ref ImageArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - command._imageArray.Get(threaded).Base.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs new file mode 100644 index 000000000..8e3ba88a8 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray +{ + struct ImageArraySetFormatsCommand : IGALCommand, IGALCommand + { + public readonly CommandType CommandType => CommandType.ImageArraySetFormats; + private TableRef _imageArray; + private int _index; + private TableRef _imageFormats; + + public void Set(TableRef imageArray, int index, TableRef imageFormats) + { + _imageArray = imageArray; + _index = index; + _imageFormats = imageFormats; + } + + public static void Run(ref ImageArraySetFormatsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedImageArray imageArray = command._imageArray.Get(threaded); + imageArray.Base.SetFormats(command._index, command._imageFormats.Get(threaded)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs new file mode 100644 index 000000000..60a6e4bf4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -0,0 +1,31 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateBufferCommand : IGALCommand, IGALCommand + { + public readonly CommandType CommandType => CommandType.CreateBuffer; + private BufferHandle _threadedHandle; + private int _size; + private BufferAccess _access; + private BufferHandle _storageHint; + + public void Set(BufferHandle threadedHandle, int size, BufferAccess access, BufferHandle storageHint) + { + _threadedHandle = threadedHandle; + _size = size; + _access = access; + _storageHint = storageHint; + } + + public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + BufferHandle hint = BufferHandle.Null; + + if (command._storageHint != BufferHandle.Null) + { + hint = threaded.Buffers.MapBuffer(command._storageHint); + } + + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, command._access, hint)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArraySeparateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArraySeparateCommand.cs deleted file mode 100644 index abeb58a06..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArraySeparateCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Ryujinx.Graphics.GAL.Multithreading.Model; -using Ryujinx.Graphics.GAL.Multithreading.Resources; -using Ryujinx.Graphics.Shader; - -namespace Ryujinx.Graphics.GAL.Multithreading.Commands -{ - struct SetImageArraySeparateCommand : IGALCommand, IGALCommand - { - public readonly CommandType CommandType => CommandType.SetImageArraySeparate; - private ShaderStage _stage; - private int _setIndex; - private TableRef _array; - - public void Set(ShaderStage stage, int setIndex, TableRef array) - { - _stage = stage; - _setIndex = setIndex; - _array = array; - } - - public static void Run(ref SetImageArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - renderer.Pipeline.SetImageArraySeparate(command._stage, command._setIndex, command._array.GetAs(threaded)?.Base); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs index 2ba9db527..243480a81 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs @@ -10,17 +10,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands private ShaderStage _stage; private int _binding; private TableRef _texture; + private Format _imageFormat; - public void Set(ShaderStage stage, int binding, TableRef texture) + public void Set(ShaderStage stage, int binding, TableRef texture, Format imageFormat) { _stage = stage; _binding = binding; _texture = texture; + _imageFormat = imageFormat; } public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs(threaded)?.Base); + renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs(threaded)?.Base, command._imageFormat); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArraySeparateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArraySeparateCommand.cs deleted file mode 100644 index b179f2e70..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArraySeparateCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Ryujinx.Graphics.GAL.Multithreading.Model; -using Ryujinx.Graphics.GAL.Multithreading.Resources; -using Ryujinx.Graphics.Shader; - -namespace Ryujinx.Graphics.GAL.Multithreading.Commands -{ - struct SetTextureArraySeparateCommand : IGALCommand, IGALCommand - { - public readonly CommandType CommandType => CommandType.SetTextureArraySeparate; - private ShaderStage _stage; - private int _setIndex; - private TableRef _array; - - public void Set(ShaderStage stage, int setIndex, TableRef array) - { - _stage = stage; - _setIndex = setIndex; - _array = array; - } - - public static void Run(ref SetTextureArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - renderer.Pipeline.SetTextureArraySeparate(command._stage, command._setIndex, command._array.GetAs(threaded)?.Base); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs index 4449566a7..3aba004df 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -1,6 +1,6 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetData; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; - public void Set(TableRef texture, TableRef> data) + public void Set(TableRef texture, TableRef> data) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs index 3619149e9..7ad709a75 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -1,6 +1,6 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetDataSlice; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; private int _layer; private int _level; - public void Set(TableRef texture, TableRef> data, int layer, int level) + public void Set(TableRef texture, TableRef> data, int layer, int level) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs index 6c6a53636..c211931bc 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -1,6 +1,6 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; private int _layer; private int _level; private Rectangle _region; - public void Set(TableRef texture, TableRef> data, int layer, int level, Rectangle region) + public void Set(TableRef texture, TableRef> data, int layer, int level, Rectangle region) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArraySetSamplersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureAndSamplerArray/TextureArraySetSamplersCommand.cs similarity index 100% rename from src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArraySetSamplersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureAndSamplerArray/TextureArraySetSamplersCommand.cs diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArraySetTexturesCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureAndSamplerArray/TextureArraySetTexturesCommand.cs similarity index 100% rename from src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArraySetTexturesCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureAndSamplerArray/TextureArraySetTexturesCommand.cs diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArrayDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArrayDisposeCommand.cs deleted file mode 100644 index fec1c48f0..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureArray/TextureArrayDisposeCommand.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Ryujinx.Graphics.GAL.Multithreading.Model; -using Ryujinx.Graphics.GAL.Multithreading.Resources; - -namespace Ryujinx.Graphics.GAL.Multithreading.Commands.TextureArray -{ - struct TextureArrayDisposeCommand : IGALCommand, IGALCommand - { - public readonly CommandType CommandType => CommandType.TextureArrayDispose; - private TableRef _textureArray; - - public void Set(TableRef textureArray) - { - _textureArray = textureArray; - } - - public static void Run(ref TextureArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - command._textureArray.Get(threaded).Base.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs index 82587c189..d26ee1fbd 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs @@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() + public void SetFormats(int index, Format[] imageFormats) { - _renderer.New().Set(Ref(this)); + _renderer.New().Set(Ref(this), index, Ref(imageFormats)); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index fa71d20b3..80003b844 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -1,6 +1,6 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.Model; +using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Resources { @@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } /// - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { _renderer.New().Set(Ref(this), Ref(data)); _renderer.QueueCommand(); } /// - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { _renderer.New().Set(Ref(this), Ref(data), layer, level); _renderer.QueueCommand(); } /// - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { _renderer.New().Set(Ref(this), Ref(data), layer, level, region); _renderer.QueueCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs index 4334c7048..82405a1f6 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs @@ -22,12 +22,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() - { - _renderer.New().Set(Ref(this)); - _renderer.QueueCommand(); - } - public void SetSamplers(int index, ISampler[] samplers) { _renderer.New().Set(Ref(this), index, Ref(samplers.ToArray())); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index deec36648..697894eb5 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetImage(ShaderStage stage, int binding, ITexture texture) + public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { - _renderer.New().Set(stage, binding, Ref(texture)); + _renderer.New().Set(stage, binding, Ref(texture), imageFormat); _renderer.QueueCommand(); } @@ -189,12 +189,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) - { - _renderer.New().Set(stage, setIndex, Ref(array)); - _renderer.QueueCommand(); - } - public void SetIndexBuffer(BufferRange buffer, IndexType type) { _renderer.New().Set(buffer, type); @@ -303,12 +297,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) - { - _renderer.New().Set(stage, setIndex, Ref(array)); - _renderer.QueueCommand(); - } - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { _renderer.New().Set(_renderer.CopySpan(buffers)); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index cc3d2e5c1..5e17bcd2c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -272,6 +272,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading return handle; } + public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) + { + BufferHandle handle = Buffers.CreateBufferHandle(); + New().Set(handle, size, access, storageHint); + QueueCommand(); + + return handle; + } + public BufferHandle CreateBuffer(nint pointer, int size) { BufferHandle handle = Buffers.CreateBufferHandle(); diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs index b7464ee12..998c046f1 100644 --- a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs +++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs @@ -74,15 +74,13 @@ namespace Ryujinx.Graphics.GAL public int ArrayLength { get; } public ResourceType Type { get; } public ResourceStages Stages { get; } - public bool Write { get; } - public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write) + public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages) { Binding = binding; ArrayLength = arrayLength; Type = type; Stages = stages; - Write = write; } public override int GetHashCode() diff --git a/src/Ryujinx.Graphics.GAL/SystemMemoryType.cs b/src/Ryujinx.Graphics.GAL/SystemMemoryType.cs deleted file mode 100644 index 532921298..000000000 --- a/src/Ryujinx.Graphics.GAL/SystemMemoryType.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ryujinx.Graphics.GAL -{ - public enum SystemMemoryType - { - /// - /// The backend manages the ownership of memory. This mode never supports host imported memory. - /// - BackendManaged, - - /// - /// Device memory has similar performance to host memory, usually because it's shared between CPU/GPU. - /// Use host memory whenever possible. - /// - UnifiedMemory, - - /// - /// GPU storage to host memory goes though a slow interconnect, but it would still be preferable to use it if the data is flushed back often. - /// Assumes constant buffer access to host memory is rather fast. - /// - DedicatedMemory, - - /// - /// GPU storage to host memory goes though a slow interconnect, that is very slow when doing access from storage. - /// When frequently accessed, copy buffers to host memory using DMA. - /// Assumes constant buffer access to host memory is rather fast. - /// - DedicatedMemorySlowStorage - } -} diff --git a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs index 79c84db01..44090291d 100644 --- a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs +++ b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs @@ -1,5 +1,6 @@ using Ryujinx.Common; using System; +using System.Numerics; namespace Ryujinx.Graphics.GAL { @@ -112,6 +113,25 @@ 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); diff --git a/src/Ryujinx.Graphics.GAL/UpscaleType.cs b/src/Ryujinx.Graphics.GAL/UpscaleType.cs index e2482faef..ca24199c4 100644 --- a/src/Ryujinx.Graphics.GAL/UpscaleType.cs +++ b/src/Ryujinx.Graphics.GAL/UpscaleType.cs @@ -5,6 +5,5 @@ namespace Ryujinx.Graphics.GAL Bilinear, Nearest, Fsr, - Area, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index cdeae0040..218db15cf 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -1,10 +1,10 @@ using Ryujinx.Common; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.Device; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; using System; +using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -276,6 +276,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma dstBaseOffset += dstStride * (yCount - 1); } + ReadOnlySpan srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true); + // If remapping is disabled, we always copy the components directly, in order. // If it's enabled, but the mapping is just XYZW, we also copy them in order. bool isIdentityRemap = !remap || @@ -287,52 +289,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount); bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount); - // Check if the source texture exists on the GPU, if it does, do a GPU side copy. - // Otherwise, we would need to flush the source texture which is costly. - // We don't expect the source to be linear in such cases, as linear source usually indicates buffer or CPU written data. - - if (completeSource && completeDest && !srcLinear && isIdentityRemap) - { - var source = memoryManager.Physical.TextureCache.FindTexture( - memoryManager, - srcGpuVa, - srcBpp, - srcStride, - src.Height, - xCount, - yCount, - srcLinear, - src.MemoryLayout.UnpackGobBlocksInY(), - src.MemoryLayout.UnpackGobBlocksInZ()); - - if (source != null && source.Height == yCount) - { - source.SynchronizeMemory(); - - var target = memoryManager.Physical.TextureCache.FindOrCreateTexture( - memoryManager, - source.Info.FormatInfo, - dstGpuVa, - xCount, - yCount, - dstStride, - dstLinear, - dst.MemoryLayout.UnpackGobBlocksInY(), - dst.MemoryLayout.UnpackGobBlocksInZ()); - - if (source.ScaleFactor != target.ScaleFactor) - { - target.PropagateScale(source); - } - - source.HostTexture.CopyTo(target.HostTexture, 0, 0); - target.SignalModified(); - return; - } - } - - ReadOnlySpan srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true); - // Try to set the texture data directly, // but only if we are doing a complete copy, // and not for block linear to linear copies, since those are typically accessed from the CPU. @@ -353,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma if (target != null) { - MemoryOwner data; + IMemoryOwner data; if (srcLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index 78099f74a..93e43ce3c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory if (target != null) { target.SynchronizeMemory(); - var dataCopy = MemoryOwner.RentCopy(data); + var dataCopy = ByteMemoryPool.RentCopy(data); target.SetData(dataCopy, 0, 0, new GAL.Rectangle(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); target.SignalModified(); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index 475d1ee4e..7f3772f44 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -5,7 +5,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Types; -using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Memory.Range; using System; using System.Collections.Generic; @@ -496,8 +495,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride; - MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect); - MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect); + MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize); + MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4); _processor.ThreedClass.DrawIndirect( topology, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs index bdb34180e..7bff1c4b8 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; namespace Ryujinx.Graphics.Gpu.Engine @@ -62,51 +61,51 @@ namespace Ryujinx.Graphics.Gpu.Engine /// /// Shader image format /// Texture format - public static FormatInfo GetFormatInfo(TextureFormat format) + public static Format GetFormat(TextureFormat format) { return format switch { #pragma warning disable IDE0055 // Disable formatting - TextureFormat.R8Unorm => new(Format.R8Unorm, 1, 1, 1, 1), - TextureFormat.R8Snorm => new(Format.R8Snorm, 1, 1, 1, 1), - TextureFormat.R8Uint => new(Format.R8Uint, 1, 1, 1, 1), - TextureFormat.R8Sint => new(Format.R8Sint, 1, 1, 1, 1), - TextureFormat.R16Float => new(Format.R16Float, 1, 1, 2, 1), - TextureFormat.R16Unorm => new(Format.R16Unorm, 1, 1, 2, 1), - TextureFormat.R16Snorm => new(Format.R16Snorm, 1, 1, 2, 1), - TextureFormat.R16Uint => new(Format.R16Uint, 1, 1, 2, 1), - TextureFormat.R16Sint => new(Format.R16Sint, 1, 1, 2, 1), - TextureFormat.R32Float => new(Format.R32Float, 1, 1, 4, 1), - TextureFormat.R32Uint => new(Format.R32Uint, 1, 1, 4, 1), - TextureFormat.R32Sint => new(Format.R32Sint, 1, 1, 4, 1), - TextureFormat.R8G8Unorm => new(Format.R8G8Unorm, 1, 1, 2, 2), - TextureFormat.R8G8Snorm => new(Format.R8G8Snorm, 1, 1, 2, 2), - TextureFormat.R8G8Uint => new(Format.R8G8Uint, 1, 1, 2, 2), - TextureFormat.R8G8Sint => new(Format.R8G8Sint, 1, 1, 2, 2), - TextureFormat.R16G16Float => new(Format.R16G16Float, 1, 1, 4, 2), - TextureFormat.R16G16Unorm => new(Format.R16G16Unorm, 1, 1, 4, 2), - TextureFormat.R16G16Snorm => new(Format.R16G16Snorm, 1, 1, 4, 2), - TextureFormat.R16G16Uint => new(Format.R16G16Uint, 1, 1, 4, 2), - TextureFormat.R16G16Sint => new(Format.R16G16Sint, 1, 1, 4, 2), - TextureFormat.R32G32Float => new(Format.R32G32Float, 1, 1, 8, 2), - TextureFormat.R32G32Uint => new(Format.R32G32Uint, 1, 1, 8, 2), - TextureFormat.R32G32Sint => new(Format.R32G32Sint, 1, 1, 8, 2), - TextureFormat.R8G8B8A8Unorm => new(Format.R8G8B8A8Unorm, 1, 1, 4, 4), - TextureFormat.R8G8B8A8Snorm => new(Format.R8G8B8A8Snorm, 1, 1, 4, 4), - TextureFormat.R8G8B8A8Uint => new(Format.R8G8B8A8Uint, 1, 1, 4, 4), - TextureFormat.R8G8B8A8Sint => new(Format.R8G8B8A8Sint, 1, 1, 4, 4), - TextureFormat.R16G16B16A16Float => new(Format.R16G16B16A16Float, 1, 1, 8, 4), - TextureFormat.R16G16B16A16Unorm => new(Format.R16G16B16A16Unorm, 1, 1, 8, 4), - TextureFormat.R16G16B16A16Snorm => new(Format.R16G16B16A16Snorm, 1, 1, 8, 4), - TextureFormat.R16G16B16A16Uint => new(Format.R16G16B16A16Uint, 1, 1, 8, 4), - TextureFormat.R16G16B16A16Sint => new(Format.R16G16B16A16Sint, 1, 1, 8, 4), - TextureFormat.R32G32B32A32Float => new(Format.R32G32B32A32Float, 1, 1, 16, 4), - TextureFormat.R32G32B32A32Uint => new(Format.R32G32B32A32Uint, 1, 1, 16, 4), - TextureFormat.R32G32B32A32Sint => new(Format.R32G32B32A32Sint, 1, 1, 16, 4), - TextureFormat.R10G10B10A2Unorm => new(Format.R10G10B10A2Unorm, 1, 1, 4, 4), - TextureFormat.R10G10B10A2Uint => new(Format.R10G10B10A2Uint, 1, 1, 4, 4), - TextureFormat.R11G11B10Float => new(Format.R11G11B10Float, 1, 1, 4, 3), - _ => FormatInfo.Invalid, + TextureFormat.R8Unorm => Format.R8Unorm, + TextureFormat.R8Snorm => Format.R8Snorm, + TextureFormat.R8Uint => Format.R8Uint, + TextureFormat.R8Sint => Format.R8Sint, + TextureFormat.R16Float => Format.R16Float, + TextureFormat.R16Unorm => Format.R16Unorm, + TextureFormat.R16Snorm => Format.R16Snorm, + TextureFormat.R16Uint => Format.R16Uint, + TextureFormat.R16Sint => Format.R16Sint, + TextureFormat.R32Float => Format.R32Float, + TextureFormat.R32Uint => Format.R32Uint, + TextureFormat.R32Sint => Format.R32Sint, + TextureFormat.R8G8Unorm => Format.R8G8Unorm, + TextureFormat.R8G8Snorm => Format.R8G8Snorm, + TextureFormat.R8G8Uint => Format.R8G8Uint, + TextureFormat.R8G8Sint => Format.R8G8Sint, + TextureFormat.R16G16Float => Format.R16G16Float, + TextureFormat.R16G16Unorm => Format.R16G16Unorm, + TextureFormat.R16G16Snorm => Format.R16G16Snorm, + TextureFormat.R16G16Uint => Format.R16G16Uint, + TextureFormat.R16G16Sint => Format.R16G16Sint, + TextureFormat.R32G32Float => Format.R32G32Float, + TextureFormat.R32G32Uint => Format.R32G32Uint, + TextureFormat.R32G32Sint => Format.R32G32Sint, + TextureFormat.R8G8B8A8Unorm => Format.R8G8B8A8Unorm, + TextureFormat.R8G8B8A8Snorm => Format.R8G8B8A8Snorm, + TextureFormat.R8G8B8A8Uint => Format.R8G8B8A8Uint, + TextureFormat.R8G8B8A8Sint => Format.R8G8B8A8Sint, + TextureFormat.R16G16B16A16Float => Format.R16G16B16A16Float, + TextureFormat.R16G16B16A16Unorm => Format.R16G16B16A16Unorm, + TextureFormat.R16G16B16A16Snorm => Format.R16G16B16A16Snorm, + TextureFormat.R16G16B16A16Uint => Format.R16G16B16A16Uint, + TextureFormat.R16G16B16A16Sint => Format.R16G16B16A16Sint, + TextureFormat.R32G32B32A32Float => Format.R32G32B32A32Float, + TextureFormat.R32G32B32A32Uint => Format.R32G32B32A32Uint, + TextureFormat.R32G32B32A32Sint => Format.R32G32B32A32Sint, + TextureFormat.R10G10B10A2Unorm => Format.R10G10B10A2Unorm, + TextureFormat.R10G10B10A2Uint => Format.R10G10B10A2Uint, + TextureFormat.R11G11B10Float => Format.R11G11B10Float, + _ => 0, #pragma warning restore IDE0055 }; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 6de50fb2e..f9cb40b0d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -438,7 +438,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw ReadOnlySpan dataBytes = MemoryMarshal.Cast(data); - BufferHandle buffer = _context.Renderer.CreateBuffer(dataBytes.Length, BufferAccess.DeviceMemory); + BufferHandle buffer = _context.Renderer.CreateBuffer(dataBytes.Length); _context.Renderer.SetBufferData(buffer, 0, dataBytes); return new IndexBuffer(buffer, count, dataBytes.Length); @@ -529,7 +529,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { if (_dummyBuffer == BufferHandle.Null) { - _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory); + _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize); _context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0); } @@ -550,7 +550,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.DeleteBuffer(_sequentialIndexBuffer); } - _sequentialIndexBuffer = _context.Renderer.CreateBuffer(count * sizeof(uint), BufferAccess.DeviceMemory); + _sequentialIndexBuffer = _context.Renderer.CreateBuffer(count * sizeof(uint)); _sequentialIndexBufferCount = count; Span data = new int[count]; @@ -583,7 +583,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.DeleteBuffer(buffer.Handle); } - buffer.Handle = _context.Renderer.CreateBuffer(newSize, BufferAccess.DeviceMemory); + buffer.Handle = _context.Renderer.CreateBuffer(newSize); buffer.Size = newSize; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 73682866b..6324e6a15 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Image; -using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -371,7 +370,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { var memoryManager = _channel.MemoryManager; - BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer); + BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size)); ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); bufferTexture.SetStorage(range); @@ -413,9 +412,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw var memoryManager = _channel.MemoryManager; ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1); - BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange( - memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign), - BufferStage.IndexBuffer); + BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign)); misalignedOffset = (int)misalign >> shift; SetIndexBufferTexture(reservations, range, format); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 56ef64c6e..d8de14de0 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (hasCount) { - var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); - var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect); + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange); + var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange); if (indexed) { @@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } else { - var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange); if (indexed) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs index effcb7bbb..e54855a8f 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs @@ -79,10 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { var field = fields[fieldIndex]; - var currentFieldOffset = (int)Marshal.OffsetOf(field.Name); - var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name); - - int sizeOfField = nextFieldOffset - currentFieldOffset; + int sizeOfField = SizeCalculator.SizeOf(field.FieldType); if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex)) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index 35051c6e0..dd55e7d1d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -415,13 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed #pragma warning disable CS0649 // Field is never assigned to public int Width; public int Height; - public ushort Depth; - public ushort Flags; - - public readonly bool UnpackIsLayered() - { - return (Flags & 1) == 0; - } + public int Depth; #pragma warning restore CS0649 } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 048d32fb7..53ea8cb27 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -393,18 +393,17 @@ namespace Ryujinx.Graphics.Gpu if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0)) { + Renderer.CreateSync(SyncNumber, strict); + foreach (var action in SyncActions) { action.SyncPreAction(syncpoint); } - foreach (var action in SyncpointActions) { action.SyncPreAction(syncpoint); } - Renderer.CreateSync(SyncNumber, strict); - SyncNumber++; SyncActions.RemoveAll(action => action.SyncAction(syncpoint)); diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index ad6c1fecb..5e66a3b54 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; @@ -47,11 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Image { private const int MinCountForDeletion = 32; private const int MaxCapacity = 2048; - private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024; - private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024; - private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024; - private const float MemoryScaleFactor = 0.50f; - private ulong _maxCacheMemoryUsage = 0; + private const ulong MaxTextureSizeCapacity = 1024 * 1024 * 1024; // MB; private readonly LinkedList _textures; private ulong _totalSize; @@ -61,25 +56,6 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly Dictionary _shortCacheLookup; - /// - /// Initializes the cache, setting the maximum texture capacity for the specified GPU context. - /// - /// - /// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`. - /// - /// The GPU context that the cache belongs to - public void Initialize(GpuContext context) - { - var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor); - - _maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity); - - if (context.Capabilities.MaximumGpuMemory == 0) - { - _maxCacheMemoryUsage = DefaultTextureSizeCapacity; - } - } - /// /// Creates a new instance of the automatic deletion cache. /// @@ -109,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Image texture.CacheNode = _textures.AddLast(texture); if (_textures.Count > MaxCapacity || - (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion)) + (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)) { RemoveLeastUsedTexture(); } @@ -134,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Image _textures.AddLast(texture.CacheNode); } - if (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion) + if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion) { RemoveLeastUsedTexture(); } diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs index b95c684e4..8a9f37bb0 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs @@ -7,11 +7,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// readonly struct FormatInfo { - /// - /// An invalid texture format. - /// - public static FormatInfo Invalid { get; } = new(0, 0, 0, 0, 0); - /// /// A default, generic RGBA8 texture format. /// @@ -28,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Must be 1 for non-compressed formats. /// - public byte BlockWidth { get; } + public int BlockWidth { get; } /// /// The block height for compressed formats. @@ -36,17 +31,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Must be 1 for non-compressed formats. /// - public byte BlockHeight { get; } + public int BlockHeight { get; } /// /// The number of bytes occupied by a single pixel in memory of the texture data. /// - public byte BytesPerPixel { get; } + public int BytesPerPixel { get; } /// /// The maximum number of components this format has defined (in RGBA order). /// - public byte Components { get; } + public int Components { get; } /// /// Whenever or not the texture format is a compressed format. Determined from block size. @@ -62,10 +57,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// The number of bytes occupied by a single pixel in memory of the texture data public FormatInfo( Format format, - byte blockWidth, - byte blockHeight, - byte bytesPerPixel, - byte components) + int blockWidth, + int blockHeight, + int bytesPerPixel, + int components) { Format = format; BlockWidth = blockWidth; diff --git a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs index b007c1591..d6a3d975b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -13,11 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// public bool IsDisposed { get; private set; } - /// - /// True if the sampler has sRGB conversion enabled, false otherwise. - /// - public bool IsSrgb { get; } - /// /// Host sampler object. /// @@ -35,8 +30,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// The Maxwell sampler descriptor public Sampler(GpuContext context, SamplerDescriptor descriptor) { - IsSrgb = descriptor.UnpackSrgb(); - MinFilter minFilter = descriptor.UnpackMinFilter(); MagFilter magFilter = descriptor.UnpackMagFilter(); diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs index 836a3260c..e04c31dfa 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs @@ -113,15 +113,6 @@ namespace Ryujinx.Graphics.Gpu.Image return (CompareOp)(((Word0 >> 10) & 7) + 1); } - /// - /// Unpacks the sampler sRGB format flag. - /// - /// True if the has sampler is sRGB conversion enabled, false otherwise - public readonly bool UnpackSrgb() - { - return (Word0 & (1 << 13)) != 0; - } - /// /// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering. /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 7ee2e5cf0..dde28dbd7 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; @@ -7,6 +6,7 @@ using Ryujinx.Graphics.Texture.Astc; using Ryujinx.Memory; using Ryujinx.Memory.Range; using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -661,7 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } - MemoryOwner result = ConvertToHostCompatibleFormat(data); + IMemoryOwner result = ConvertToHostCompatibleFormat(data); if (ScaleFactor != 1f && AllowScaledSetData()) { @@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Uploads new texture data to the host GPU. /// /// New data - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { BlacklistScale(); @@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// New data /// Target layer /// Target level - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { BlacklistScale(); @@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Target layer /// Target level /// Target sub-region of the texture to update - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { BlacklistScale(); @@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Mip level to convert /// True to convert a single slice /// Converted data - public MemoryOwner ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) + public IMemoryOwner ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) { int width = Info.Width; int height = Info.Height; @@ -754,7 +754,7 @@ namespace Ryujinx.Graphics.Gpu.Image int sliceDepth = single ? 1 : depth; - MemoryOwner linear; + IMemoryOwner linear; if (Info.IsLinear) { @@ -787,7 +787,7 @@ namespace Ryujinx.Graphics.Gpu.Image data); } - MemoryOwner result = linear; + IMemoryOwner result = linear; // Handle compressed cases not supported by the host: // - ASTC is usually not supported on desktop cards. @@ -805,7 +805,7 @@ namespace Ryujinx.Graphics.Gpu.Image sliceDepth, levels, layers, - out MemoryOwner decoded)) + out IMemoryOwner decoded)) { string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}"; @@ -831,19 +831,19 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.Etc2RgbaUnorm: using (result) { - return ETC2Decoder.DecodeRgba(result.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodeRgba(result.Memory.Span, width, height, sliceDepth, levels, layers); } case Format.Etc2RgbPtaSrgb: case Format.Etc2RgbPtaUnorm: using (result) { - return ETC2Decoder.DecodePta(result.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodePta(result.Memory.Span, width, height, sliceDepth, levels, layers); } case Format.Etc2RgbSrgb: case Format.Etc2RgbUnorm: using (result) { - return ETC2Decoder.DecodeRgb(result.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodeRgb(result.Memory.Span, width, height, sliceDepth, levels, layers); } } } @@ -855,43 +855,43 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.Bc1RgbaUnorm: using (result) { - return BCnDecoder.DecodeBC1(result.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC1(result.Memory.Span, width, height, sliceDepth, levels, layers); } case Format.Bc2Srgb: case Format.Bc2Unorm: using (result) { - return BCnDecoder.DecodeBC2(result.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC2(result.Memory.Span, width, height, sliceDepth, levels, layers); } case Format.Bc3Srgb: case Format.Bc3Unorm: using (result) { - return BCnDecoder.DecodeBC3(result.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC3(result.Memory.Span, width, height, sliceDepth, levels, layers); } case Format.Bc4Snorm: case Format.Bc4Unorm: using (result) { - return BCnDecoder.DecodeBC4(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm); + return BCnDecoder.DecodeBC4(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm); } case Format.Bc5Snorm: case Format.Bc5Unorm: using (result) { - return BCnDecoder.DecodeBC5(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm); + return BCnDecoder.DecodeBC5(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm); } case Format.Bc6HSfloat: case Format.Bc6HUfloat: using (result) { - return BCnDecoder.DecodeBC6(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat); + return BCnDecoder.DecodeBC6(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat); } case Format.Bc7Srgb: case Format.Bc7Unorm: using (result) { - return BCnDecoder.DecodeBC7(result.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC7(result.Memory.Span, width, height, sliceDepth, levels, layers); } } } @@ -899,7 +899,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (result) { - var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Span, width); + var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Memory.Span, width); if (_context.Capabilities.SupportsR4G4B4A4Format) { @@ -909,7 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (converted) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Memory.Span, width); } } } @@ -920,7 +920,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (result) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width); } } } @@ -932,24 +932,24 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.R5G6B5Unorm: using (result) { - return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Span, width); + return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width); } case Format.B5G5R5A1Unorm: case Format.R5G5B5X1Unorm: case Format.R5G5B5A1Unorm: using (result) { - return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Span, width, Format == Format.R5G5B5X1Unorm); + return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Memory.Span, width, Format == Format.R5G5B5X1Unorm); } case Format.A1B5G5R5Unorm: using (result) { - return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Span, width); + return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width); } case Format.R4G4B4A4Unorm: using (result) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs index e9930405b..ba895c60a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs @@ -17,12 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// For images, indicates the format specified on the shader. /// - public FormatInfo FormatInfo { get; } - - /// - /// Shader texture host set index. - /// - public int Set { get; } + public Format Format { get; } /// /// Shader texture host binding point. @@ -58,18 +53,16 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs the texture binding information structure. /// /// The shader sampler target type - /// Format of the image as declared on the shader - /// Shader texture host set index + /// Format of the image as declared on the shader /// The shader texture binding point /// For array of textures, this indicates the length of the array. A value of one indicates it is not an array /// Constant buffer slot where the texture handle is located /// The shader texture handle (read index into the texture constant buffer) /// The texture's usage flags, indicating how it is used in the shader - public TextureBindingInfo(Target target, FormatInfo formatInfo, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags) + public TextureBindingInfo(Target target, Format format, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags) { Target = target; - FormatInfo = formatInfo; - Set = set; + Format = format; Binding = binding; ArrayLength = arrayLength; CbufSlot = cbufSlot; @@ -81,7 +74,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs the texture binding information structure. /// /// The shader sampler target type - /// Shader texture host set index /// The shader texture binding point /// For array of textures, this indicates the length of the array. A value of one indicates it is not an array /// Constant buffer slot where the texture handle is located @@ -90,13 +82,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// Indicates that the binding is for a sampler public TextureBindingInfo( Target target, - int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags, - bool isSamplerOnly) : this(target, FormatInfo.Invalid, set, binding, arrayLength, cbufSlot, handle, flags) + bool isSamplerOnly) : this(target, 0, binding, arrayLength, cbufSlot, handle, flags) { IsSamplerOnly = isSamplerOnly; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs index 72bac75e5..7e486e0a8 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs @@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if any used entries of the pool might have been modified, false otherwise public bool SamplerPoolModified() { - return SamplerPool != null && SamplerPool.WasModified(ref _samplerPoolSequence); + return SamplerPool.WasModified(ref _samplerPoolSequence); } } @@ -516,15 +516,12 @@ namespace Ryujinx.Graphics.Gpu.Image } // Check if any of our cached samplers changed on the pool. - if (SamplerPool != null) + foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds) { - foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds) + if (SamplerPool.GetCachedItem(samplerId) != sampler || + (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor))) { - if (SamplerPool.GetCachedItem(samplerId) != sampler || - (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor))) - { - return true; - } + return true; } } @@ -569,7 +566,7 @@ namespace Ryujinx.Graphics.Gpu.Image int stageIndex, int textureBufferIndex, SamplerIndex samplerIndex, - in TextureBindingInfo bindingInfo) + TextureBindingInfo bindingInfo) { Update(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage: false, samplerIndex, bindingInfo); } @@ -582,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Shader stage index where the array is used /// Texture constant buffer index /// Array binding information - public void UpdateImageArray(TexturePool texturePool, ShaderStage stage, int stageIndex, int textureBufferIndex, in TextureBindingInfo bindingInfo) + public void UpdateImageArray(TexturePool texturePool, ShaderStage stage, int stageIndex, int textureBufferIndex, TextureBindingInfo bindingInfo) { Update(texturePool, null, stage, stageIndex, textureBufferIndex, isImage: true, SamplerIndex.ViaHeaderIndex, bindingInfo); } @@ -606,7 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Image int textureBufferIndex, bool isImage, SamplerIndex samplerIndex, - in TextureBindingInfo bindingInfo) + TextureBindingInfo bindingInfo) { if (IsDirectHandleType(bindingInfo.Handle)) { @@ -626,7 +623,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Shader stage where the array is used /// Whether the array is a image or texture array /// Array binding information - private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, in TextureBindingInfo bindingInfo) + private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, TextureBindingInfo bindingInfo) { CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry); @@ -641,11 +638,11 @@ namespace Ryujinx.Graphics.Gpu.Image if (isImage) { - SetImageArray(stage, bindingInfo, entry.ImageArray); + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); } else { - SetTextureArray(stage, bindingInfo, entry.TextureArray); + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); } return; @@ -659,6 +656,7 @@ namespace Ryujinx.Graphics.Gpu.Image int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1; length = Math.Min(length, bindingInfo.ArrayLength); + Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null; ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength]; ITexture[] textures = new ITexture[bindingInfo.ArrayLength]; @@ -673,7 +671,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture); + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture); if (texture != null) { @@ -696,6 +694,8 @@ namespace Ryujinx.Graphics.Gpu.Image ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); ISampler hostSampler = sampler?.GetHostSampler(texture); + Format format = bindingInfo.Format; + if (hostTexture != null && texture.Target == Target.TextureBuffer) { // Ensure that the buffer texture is using the correct buffer as storage. @@ -703,15 +703,26 @@ namespace Ryujinx.Graphics.Gpu.Image // to ensure we're not using a old buffer that was already deleted. if (isImage) { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index); + if (format == 0 && texture != null) + { + format = texture.Format; + } + + _channel.BufferManager.SetBufferTextureStorage(entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format); } else { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index); + _channel.BufferManager.SetBufferTextureStorage(entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format); } } else if (isImage) { + if (format == 0 && texture != null) + { + format = texture.Format; + } + + formats[index] = format; textures[index] = hostTexture; } else @@ -723,16 +734,17 @@ namespace Ryujinx.Graphics.Gpu.Image if (isImage) { + entry.ImageArray.SetFormats(0, formats); entry.ImageArray.SetImages(0, textures); - SetImageArray(stage, bindingInfo, entry.ImageArray); + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); } else { entry.TextureArray.SetSamplers(0, samplers); entry.TextureArray.SetTextures(0, textures); - SetTextureArray(stage, bindingInfo, entry.TextureArray); + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); } } @@ -755,7 +767,7 @@ namespace Ryujinx.Graphics.Gpu.Image int textureBufferIndex, bool isImage, SamplerIndex samplerIndex, - in TextureBindingInfo bindingInfo) + TextureBindingInfo bindingInfo) { (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex); @@ -788,11 +800,11 @@ namespace Ryujinx.Graphics.Gpu.Image if (isImage) { - SetImageArray(stage, bindingInfo, entry.ImageArray); + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); } else { - SetTextureArray(stage, bindingInfo, entry.TextureArray); + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); } return; @@ -817,11 +829,11 @@ namespace Ryujinx.Graphics.Gpu.Image if (isImage) { - SetImageArray(stage, bindingInfo, entry.ImageArray); + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); } else { - SetTextureArray(stage, bindingInfo, entry.TextureArray); + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); } return; @@ -848,6 +860,7 @@ namespace Ryujinx.Graphics.Gpu.Image entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer); + Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null; ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength]; ITexture[] textures = new ITexture[bindingInfo.ArrayLength]; @@ -867,7 +880,7 @@ namespace Ryujinx.Graphics.Gpu.Image samplerId = TextureHandle.UnpackSamplerId(packedId); } - ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture); + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture); if (texture != null) { @@ -886,19 +899,15 @@ 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 = null; + ISampler hostSampler = sampler?.GetHostSampler(texture); - 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; if (hostTexture != null && texture.Target == Target.TextureBuffer) { @@ -907,15 +916,26 @@ namespace Ryujinx.Graphics.Gpu.Image // to ensure we're not using a old buffer that was already deleted. if (isImage) { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index); + if (format == 0 && texture != null) + { + format = texture.Format; + } + + _channel.BufferManager.SetBufferTextureStorage(entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format); } else { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index); + _channel.BufferManager.SetBufferTextureStorage(entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format); } } else if (isImage) { + if (format == 0 && texture != null) + { + format = texture.Format; + } + + formats[index] = format; textures[index] = hostTexture; } else @@ -927,52 +947,17 @@ namespace Ryujinx.Graphics.Gpu.Image if (isImage) { + entry.ImageArray.SetFormats(0, formats); entry.ImageArray.SetImages(0, textures); - SetImageArray(stage, bindingInfo, entry.ImageArray); + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); } else { entry.TextureArray.SetSamplers(0, samplers); entry.TextureArray.SetTextures(0, textures); - SetTextureArray(stage, bindingInfo, entry.TextureArray); - } - } - - /// - /// Updates a texture array binding on the host. - /// - /// Shader stage where the array is used - /// Array binding information - /// Texture array - private void SetTextureArray(ShaderStage stage, in TextureBindingInfo bindingInfo, ITextureArray array) - { - if (bindingInfo.Set >= _context.Capabilities.ExtraSetBaseIndex && _context.Capabilities.MaximumExtraSets != 0) - { - _context.Renderer.Pipeline.SetTextureArraySeparate(stage, bindingInfo.Set, array); - } - else - { - _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, array); - } - } - - /// - /// Updates a image array binding on the host. - /// - /// Shader stage where the array is used - /// Array binding information - /// Image array - private void SetImageArray(ShaderStage stage, in TextureBindingInfo bindingInfo, IImageArray array) - { - if (bindingInfo.Set >= _context.Capabilities.ExtraSetBaseIndex && _context.Capabilities.MaximumExtraSets != 0) - { - _context.Renderer.Pipeline.SetImageArraySeparate(stage, bindingInfo.Set, array); - } - else - { - _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, array); + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); } } @@ -988,7 +973,7 @@ namespace Ryujinx.Graphics.Gpu.Image private CacheEntry GetOrAddEntry( TexturePool texturePool, SamplerPool samplerPool, - in TextureBindingInfo bindingInfo, + TextureBindingInfo bindingInfo, bool isImage, out bool isNew) { @@ -1030,7 +1015,7 @@ namespace Ryujinx.Graphics.Gpu.Image private CacheEntryFromBuffer GetOrAddEntry( TexturePool texturePool, SamplerPool samplerPool, - in TextureBindingInfo bindingInfo, + TextureBindingInfo bindingInfo, bool isImage, ref BufferBounds textureBufferBounds, out bool isNew) @@ -1092,15 +1077,6 @@ namespace Ryujinx.Graphics.Gpu.Image nextNode = nextNode.Next; _cacheFromBuffer.Remove(toRemove.Value.Key); _lruCache.Remove(toRemove); - - if (toRemove.Value.Key.IsImage) - { - toRemove.Value.ImageArray.Dispose(); - } - else - { - toRemove.Value.TextureArray.Dispose(); - } } } @@ -1112,20 +1088,11 @@ namespace Ryujinx.Graphics.Gpu.Image { List keysToRemove = null; - foreach ((CacheEntryFromPoolKey key, CacheEntry entry) in _cacheFromPool) + foreach (CacheEntryFromPoolKey key in _cacheFromPool.Keys) { if (key.MatchesPool(pool)) { (keysToRemove ??= new()).Add(key); - - if (key.IsImage) - { - entry.ImageArray.Dispose(); - } - else - { - entry.TextureArray.Dispose(); - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index f96ddfb1b..9f1f60d95 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -187,9 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Image { (TexturePool texturePool, SamplerPool samplerPool) = GetPools(); - Sampler sampler = samplerPool?.Get(samplerId); - - return (texturePool.Get(textureId, sampler?.IsSrgb ?? true), sampler); + return (texturePool.Get(textureId), samplerPool.Get(samplerId)); } /// @@ -510,12 +508,12 @@ namespace Ryujinx.Graphics.Gpu.Image state.TextureHandle = textureId; state.SamplerHandle = samplerId; - Sampler sampler = samplerPool?.Get(samplerId); - - ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, sampler?.IsSrgb ?? true, out Texture texture); + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture); specStateMatches &= specState.MatchesTexture(stage, index, descriptor); + Sampler sampler = samplerPool?.Get(samplerId); + ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); ISampler hostSampler = sampler?.GetHostSampler(texture); @@ -524,7 +522,7 @@ namespace Ryujinx.Graphics.Gpu.Image // Ensure that the buffer texture is using the correct buffer as storage. // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false); + _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, bindingInfo.Format, false); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; @@ -618,7 +616,6 @@ namespace Ryujinx.Graphics.Gpu.Image if (!poolModified && state.TextureHandle == textureId && - state.ImageFormat == bindingInfo.FormatInfo.Format && state.CachedTexture != null && state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence) { @@ -632,22 +629,26 @@ namespace Ryujinx.Graphics.Gpu.Image cachedTexture.SignalModified(); } - if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)) + Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format; + + if (state.ImageFormat != format || + ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && + UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))) { ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target); state.Texture = hostTextureRebind; + state.ImageFormat = format; - _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind); + _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format); } continue; } state.TextureHandle = textureId; - state.ImageFormat = bindingInfo.FormatInfo.Format; - ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture); + ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture); specStateMatches &= specState.MatchesImage(stage, index, descriptor); @@ -659,7 +660,14 @@ namespace Ryujinx.Graphics.Gpu.Image // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true); + Format format = bindingInfo.Format; + + if (format == 0 && texture != null) + { + format = texture.Format; + } + + _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, format, true); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; @@ -681,7 +689,16 @@ namespace Ryujinx.Graphics.Gpu.Image { state.Texture = hostTexture; - _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture); + Format format = bindingInfo.Format; + + if (format == 0 && texture != null) + { + format = texture.Format; + } + + state.ImageFormat = format; + + _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format); } state.CachedTexture = texture; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 1587e2018..b9ff060e2 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -68,14 +68,6 @@ namespace Ryujinx.Graphics.Gpu.Image _cache = new AutoDeleteCache(); } - /// - /// Initializes the cache, setting the maximum texture capacity for the specified GPU context. - /// - public void Initialize() - { - _cache.Initialize(_context); - } - /// /// Handles marking of textures written to a memory region being (partially) remapped. /// @@ -355,53 +347,6 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } - /// - /// Tries to find an existing texture, or create a new one if not found. - /// - /// GPU memory manager where the texture is mapped - /// Format of the texture - /// GPU virtual address of the texture - /// Texture width in bytes - /// Texture height - /// Texture stride if linear, otherwise ignored - /// Indicates if the texture is linear or block linear - /// GOB blocks in Y for block linear textures - /// GOB blocks in Z for 3D block linear textures - /// The texture - public Texture FindOrCreateTexture( - MemoryManager memoryManager, - FormatInfo formatInfo, - ulong gpuAddress, - int xCount, - int yCount, - int stride, - bool isLinear, - int gobBlocksInY, - int gobBlocksInZ) - { - TextureInfo info = new( - gpuAddress, - xCount / formatInfo.BytesPerPixel, - yCount, - 1, - 1, - 1, - 1, - stride, - isLinear, - gobBlocksInY, - gobBlocksInZ, - 1, - Target.Texture2D, - formatInfo); - - Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.ForCopy, info, 0, sizeHint: new Size(xCount, yCount, 1)); - - texture?.SynchronizeMemory(); - - return texture; - } - /// /// Tries to find an existing texture, or create a new one if not found. /// @@ -523,11 +468,13 @@ namespace Ryujinx.Graphics.Gpu.Image int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); - layered &= size.UnpackIsLayered(); - Target target; - if ((samplesInX | samplesInY) != 1) + if (dsState.MemoryLayout.UnpackIsTarget3D()) + { + target = Target.Texture3D; + } + else if ((samplesInX | samplesInY) != 1) { target = size.Depth > 1 && layered ? Target.Texture2DMultisampleArray diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 8bed6363b..3cdeac9c5 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -739,8 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image } return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) || - (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm) || - (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R32Uint); + (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 526fc0c24..4e1133d1a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; @@ -6,6 +5,7 @@ using Ryujinx.Memory; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; +using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image ReadOnlySpan data = dataSpan[(offset - spanBase)..]; - MemoryOwner result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); + IMemoryOwner result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level); } @@ -645,7 +645,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - _flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.HostMemory); + _flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent); _flushBufferImported = false; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index be7cb0b89..a4035577d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -6,7 +6,6 @@ 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 @@ -75,76 +74,6 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly ConcurrentQueue _dereferenceQueue = new(); private TextureDescriptor _defaultDescriptor; - /// - /// List of textures that shares the same memory region, but have different formats. - /// - private class TextureAliasList - { - /// - /// Alias texture. - /// - /// Texture format - /// Texture - private readonly record struct Alias(Format Format, Texture Texture); - - /// - /// List of texture aliases. - /// - private readonly List _aliases; - - /// - /// Creates a new instance of the texture alias list. - /// - public TextureAliasList() - { - _aliases = new List(); - } - - /// - /// Adds a new texture alias. - /// - /// Alias format - /// Alias texture - public void Add(Format format, Texture texture) - { - _aliases.Add(new Alias(format, texture)); - texture.IncrementReferenceCount(); - } - - /// - /// Finds a texture with the requested format, or returns null if not found. - /// - /// Format to find - /// Texture with the requested format, or null if not found - public Texture Find(Format format) - { - foreach (var alias in _aliases) - { - if (alias.Format == format) - { - return alias.Texture; - } - } - - return null; - } - - /// - /// Removes all alias textures. - /// - public void Destroy() - { - foreach (var entry in _aliases) - { - entry.Texture.DecrementReferenceCount(); - } - - _aliases.Clear(); - } - } - - private readonly Dictionary _aliasLists; - /// /// Linked list node used on the texture pool cache. /// @@ -165,7 +94,6 @@ namespace Ryujinx.Graphics.Gpu.Image public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId) { _channel = channel; - _aliasLists = new Dictionary(); } /// @@ -186,13 +114,14 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture == null) { + TextureInfo info = GetInfo(descriptor, out int layerSize); + // The dereference queue can put our texture back on the cache. if ((texture = ProcessDereferenceQueue(id)) != null) { return ref descriptor; } - TextureInfo info = GetInfo(descriptor, out int layerSize); texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); // If this happens, then the texture address is invalid, we can't add it to the cache. @@ -227,17 +156,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// ID of the texture. This is effectively a zero-based index /// The texture with the given ID public override Texture Get(int id) - { - return Get(id, srgbSampler: true); - } - - /// - /// Gets the texture with the given ID. - /// - /// ID of the texture. This is effectively a zero-based index - /// Whether the texture is being accessed with a sampler that has sRGB conversion enabled - /// The texture with the given ID - public Texture Get(int id, bool srgbSampler) { if ((uint)id >= Items.Length) { @@ -251,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Image SynchronizeMemory(); } - GetForBinding(id, srgbSampler, out Texture texture); + GetInternal(id, out Texture texture); return texture; } @@ -263,10 +181,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// This method assumes that the pool has been manually synchronized before doing binding. /// /// ID of the texture. This is effectively a zero-based index - /// Whether the texture is being accessed with a sampler that has sRGB conversion enabled /// The texture with the given ID /// The texture descriptor with the given ID - public ref readonly TextureDescriptor GetForBinding(int id, bool srgbSampler, out Texture texture) + public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture) { if ((uint)id >= Items.Length) { @@ -276,66 +193,9 @@ namespace Ryujinx.Graphics.Gpu.Image // When getting for binding, assume the pool has already been synchronized. - if (!srgbSampler) - { - // If the sampler does not have the sRGB bit enabled, then the texture can't use a sRGB format. - ref readonly TextureDescriptor tempDescriptor = ref GetDescriptorRef(id); - - if (tempDescriptor.UnpackSrgb() && FormatTable.TryGetTextureFormat(tempDescriptor.UnpackFormat(), isSrgb: false, out FormatInfo formatInfo)) - { - // Get a view of the texture with the right format. - return ref GetForBinding(id, formatInfo, out texture); - } - } - return ref GetInternal(id, out texture); } - /// - /// Gets the texture descriptor and texture with the given ID. - /// - /// - /// This method assumes that the pool has been manually synchronized before doing binding. - /// - /// ID of the texture. This is effectively a zero-based index - /// Texture format information - /// The texture with the given ID - /// The texture descriptor with the given ID - public ref readonly TextureDescriptor GetForBinding(int id, FormatInfo formatInfo, out Texture texture) - { - if ((uint)id >= Items.Length) - { - texture = null; - return ref _defaultDescriptor; - } - - ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture); - - if (texture != null && formatInfo.Format != 0 && texture.Format != formatInfo.Format) - { - if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList)) - { - _aliasLists.Add(texture, aliasList = new TextureAliasList()); - } - - texture = aliasList.Find(formatInfo.Format); - - if (texture == null) - { - TextureInfo info = GetInfo(descriptor, out int layerSize); - info = ChangeFormat(info, formatInfo); - texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); - - if (texture != null) - { - aliasList.Add(formatInfo.Format, texture); - } - } - } - - return ref descriptor; - } - /// /// Checks if the pool was modified, and returns the last sequence number where a modification was detected. /// @@ -373,7 +233,6 @@ namespace Ryujinx.Graphics.Gpu.Image else { texture.DecrementReferenceCount(); - RemoveAliasList(texture); } } @@ -467,8 +326,6 @@ namespace Ryujinx.Graphics.Gpu.Image { texture.DecrementReferenceCount(); } - - RemoveAliasList(texture); } return null; @@ -511,7 +368,6 @@ namespace Ryujinx.Graphics.Gpu.Image if (Interlocked.Exchange(ref Items[id], null) != null) { texture.DecrementReferenceCount(this, id); - RemoveAliasList(texture); } } } @@ -634,8 +490,6 @@ 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(); @@ -686,34 +540,6 @@ namespace Ryujinx.Graphics.Gpu.Image swizzleA); } - /// - /// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions. - /// - /// Number of texture dimensions (1D, 2D, 3D, Cube, etc) - /// Width of the texture - /// Height of the texture, ignored for 1D textures - /// Depth of the texture for 3D textures, otherwise ignored - /// Original amount of mipmap levels - /// Clamped mipmap levels - 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); - } - /// /// 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. @@ -765,57 +591,6 @@ namespace Ryujinx.Graphics.Gpu.Image component == SwizzleComponent.Green; } - /// - /// Changes the format on the texture information structure, and also adjusts the width for the new format if needed. - /// - /// Texture information - /// New format - /// Texture information with the new format - private static TextureInfo ChangeFormat(in TextureInfo info, FormatInfo dstFormat) - { - int width = info.Width; - - if (info.FormatInfo.BytesPerPixel != dstFormat.BytesPerPixel) - { - int stride = width * info.FormatInfo.BytesPerPixel; - width = stride / dstFormat.BytesPerPixel; - } - - return new TextureInfo( - info.GpuAddress, - width, - info.Height, - info.DepthOrLayers, - info.Levels, - info.SamplesInX, - info.SamplesInY, - info.Stride, - info.IsLinear, - info.GobBlocksInY, - info.GobBlocksInZ, - info.GobBlocksInTileX, - info.Target, - dstFormat, - info.DepthStencilMode, - info.SwizzleR, - info.SwizzleG, - info.SwizzleB, - info.SwizzleA); - } - - /// - /// Removes all aliases for a texture. - /// - /// Texture to have the aliases removed - private void RemoveAliasList(Texture texture) - { - if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList)) - { - _aliasLists.Remove(texture); - aliasList.Destroy(); - } - } - /// /// Decrements the reference count of the texture. /// This indicates that the texture pool is not using it anymore. @@ -823,11 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture to be deleted protected override void Delete(Texture item) { - if (item != null) - { - item.DecrementReferenceCount(this); - RemoveAliasList(item); - } + item?.DecrementReferenceCount(this); } public override void Dispose() diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index e060e0b4f..d293060b5 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -10,8 +10,6 @@ using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory { - delegate void BufferFlushAction(ulong address, ulong size, ulong syncNumber); - /// /// Buffer, used to store vertex and index data, uniform and storage buffers, and others. /// @@ -25,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Host buffer handle. /// - public BufferHandle Handle { get; private set; } + public BufferHandle Handle { get; } /// /// Start address of the buffer in guest memory. @@ -62,17 +60,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// private BufferModifiedRangeList _modifiedRanges = null; - /// - /// A structure that is used to flush buffer data back to a host mapped buffer for cached readback. - /// Only used if the buffer data is explicitly owned by device local memory. - /// - private BufferPreFlush _preFlush = null; - - /// - /// Usage tracking state that determines what type of backing the buffer should use. - /// - public BufferBackingState BackingState; - private readonly MultiRegionHandle _memoryTrackingGranular; private readonly RegionHandle _memoryTracking; @@ -100,7 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Physical memory where the buffer is mapped /// Start address of the buffer /// Size of the buffer in bytes - /// The type of usage that created the buffer /// Indicates if the buffer can be used in a sparse buffer mapping /// Buffers which this buffer contains, and will inherit tracking handles from public Buffer( @@ -108,7 +94,6 @@ namespace Ryujinx.Graphics.Gpu.Memory PhysicalMemory physicalMemory, ulong address, ulong size, - BufferStage stage, bool sparseCompatible, IEnumerable baseBuffers = null) { @@ -118,11 +103,9 @@ namespace Ryujinx.Graphics.Gpu.Memory Size = size; SparseCompatible = sparseCompatible; - BackingState = new BufferBackingState(_context, this, stage, baseBuffers); + BufferAccess access = sparseCompatible ? BufferAccess.SparseCompatible : BufferAccess.Default; - BufferAccess access = BackingState.SwitchAccess(this); - - Handle = context.Renderer.CreateBuffer((int)size, access); + Handle = context.Renderer.CreateBuffer((int)size, access, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null); _useGranular = size > GranularBufferThreshold; @@ -178,29 +161,6 @@ namespace Ryujinx.Graphics.Gpu.Memory _virtualDependenciesLock = new ReaderWriterLockSlim(); } - /// - /// Recreates the backing buffer based on the desired access type - /// reported by the backing state struct. - /// - private void ChangeBacking() - { - BufferAccess access = BackingState.SwitchAccess(this); - - BufferHandle newHandle = _context.Renderer.CreateBuffer((int)Size, access); - - _context.Renderer.Pipeline.CopyBuffer(Handle, newHandle, 0, 0, (int)Size); - - _modifiedRanges?.SelfMigration(); - - // If swtiching from device local to host mapped, pre-flushing data no longer makes sense. - // This is set to null and disposed when the migration fully completes. - _preFlush = null; - - Handle = newHandle; - - _physicalMemory.BufferCache.BufferBackingChanged(this); - } - /// /// Gets a sub-range from the buffer, from a start address til a page boundary after the given size. /// @@ -286,7 +246,6 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - BackingState.RecordSet(); _context.Renderer.SetBufferData(Handle, 0, _physicalMemory.GetSpan(Address, (int)Size)); CopyToDependantVirtualBuffers(); } @@ -324,35 +283,15 @@ namespace Ryujinx.Graphics.Gpu.Memory _modifiedRanges ??= new BufferModifiedRangeList(_context, this, Flush); } - /// - /// Checks if a backing change is deemed necessary from the given usage. - /// If it is, queues a backing change to happen on the next sync action. - /// - /// Buffer stage that can change backing type - private void TryQueueBackingChange(BufferStage stage) - { - if (BackingState.ShouldChangeBacking(stage)) - { - if (!_syncActionRegistered) - { - _context.RegisterSyncAction(this); - _syncActionRegistered = true; - } - } - } - /// /// Signal that the given region of the buffer has been modified. /// /// The start address of the modified region /// The size of the modified region - /// Buffer stage that triggered the modification - public void SignalModified(ulong address, ulong size, BufferStage stage) + public void SignalModified(ulong address, ulong size) { EnsureRangeList(); - TryQueueBackingChange(stage); - _modifiedRanges.SignalModified(address, size); if (!_syncActionRegistered) @@ -372,37 +311,6 @@ namespace Ryujinx.Graphics.Gpu.Memory _modifiedRanges?.Clear(address, size); } - /// - /// Action to be performed immediately before sync is created. - /// This will copy any buffer ranges designated for pre-flushing. - /// - /// True if the action is a guest syncpoint - public void SyncPreAction(bool syncpoint) - { - if (_referenceCount == 0) - { - return; - } - - if (BackingState.ShouldChangeBacking()) - { - ChangeBacking(); - } - - if (BackingState.IsDeviceLocal) - { - _preFlush ??= new BufferPreFlush(_context, this, FlushImpl); - - if (_preFlush.ShouldCopy) - { - _modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, (address, size) => - { - _preFlush.CopyModified(address, size); - }); - } - } - } - /// /// Action to be performed when a syncpoint is reached after modification. /// This will register read/write tracking to flush the buffer from GPU when its memory is used. @@ -558,8 +466,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size of the modified region private void LoadRegion(ulong mAddress, ulong mSize) { - BackingState.RecordSet(); - int offset = (int)(mAddress - Address); _context.Renderer.SetBufferData(Handle, offset, _physicalMemory.GetSpan(mAddress, (int)mSize)); @@ -633,84 +539,18 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Flushes a range of the buffer. /// This writes the range data back into guest memory. /// - /// Buffer handle to flush data from /// Start address of the range /// Size in bytes of the range - private void FlushImpl(BufferHandle handle, ulong address, ulong size) + public void Flush(ulong address, ulong size) { int offset = (int)(address - Address); - using PinnedSpan data = _context.Renderer.GetBufferData(handle, offset, (int)size); + using PinnedSpan data = _context.Renderer.GetBufferData(Handle, offset, (int)size); // TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers. _physicalMemory.WriteUntracked(address, CopyFromDependantVirtualBuffers(data.Get(), address, size)); } - /// - /// Flushes a range of the buffer. - /// This writes the range data back into guest memory. - /// - /// Start address of the range - /// Size in bytes of the range - private void FlushImpl(ulong address, ulong size) - { - FlushImpl(Handle, address, size); - } - - /// - /// Flushes a range of the buffer from the most optimal source. - /// This writes the range data back into guest memory. - /// - /// Start address of the range - /// Size in bytes of the range - /// Sync number waited for before flushing the data - public void Flush(ulong address, ulong size, ulong syncNumber) - { - BackingState.RecordFlush(); - - BufferPreFlush preFlush = _preFlush; - - if (preFlush != null) - { - preFlush.FlushWithAction(address, size, syncNumber); - } - else - { - FlushImpl(address, size); - } - } - /// - /// Gets an action that disposes the backing buffer using its current handle. - /// Useful for deleting an old copy of the buffer after the handle changes. - /// - /// An action that flushes data from the specified range, using the buffer handle at the time the method is generated - public Action GetSnapshotDisposeAction() - { - BufferHandle handle = Handle; - BufferPreFlush preFlush = _preFlush; - - return () => - { - _context.Renderer.DeleteBuffer(handle); - preFlush?.Dispose(); - }; - } - - /// - /// Gets an action that flushes a range of the buffer using its current handle. - /// Useful for flushing data from old copies of the buffer after the handle changes. - /// - /// An action that flushes data from the specified range, using the buffer handle at the time the method is generated - public BufferFlushAction GetSnapshotFlushAction() - { - BufferHandle handle = Handle; - - return (ulong address, ulong size, ulong _) => - { - FlushImpl(handle, address, size); - }; - } - /// /// Align a given address and size region to page boundaries. /// @@ -1017,8 +857,6 @@ namespace Ryujinx.Graphics.Gpu.Memory _modifiedRanges?.Clear(); _context.Renderer.DeleteBuffer(Handle); - _preFlush?.Dispose(); - _preFlush = null; UnmappedSequence++; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs deleted file mode 100644 index 3f65131e6..000000000 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs +++ /dev/null @@ -1,294 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu.Memory -{ - /// - /// Type of backing memory. - /// In ascending order of priority when merging multiple buffer backing states. - /// - internal enum BufferBackingType - { - HostMemory, - DeviceMemory, - DeviceMemoryWithFlush - } - - /// - /// Keeps track of buffer usage to decide what memory heap that buffer memory is placed on. - /// Dedicated GPUs prefer certain types of resources to be device local, - /// and if we need data to be read back, we might prefer that they're in host memory. - /// - /// The measurements recorded here compare to a set of heruristics (thresholds and conditions) - /// that appear to produce good performance in most software. - /// - internal struct BufferBackingState - { - private const int DeviceLocalSizeThreshold = 256 * 1024; // 256kb - - private const int SetCountThreshold = 100; - private const int WriteCountThreshold = 50; - private const int FlushCountThreshold = 5; - private const int DeviceLocalForceExpiry = 100; - - public readonly bool IsDeviceLocal => _activeType != BufferBackingType.HostMemory; - - private readonly SystemMemoryType _systemMemoryType; - private BufferBackingType _activeType; - private BufferBackingType _desiredType; - - private bool _canSwap; - - private int _setCount; - private int _writeCount; - private int _flushCount; - private int _flushTemp; - private int _lastFlushWrite; - private int _deviceLocalForceCount; - - private readonly int _size; - - /// - /// Initialize the buffer backing state for a given parent buffer. - /// - /// GPU context - /// Parent buffer - /// Initial buffer stage - /// Buffers to inherit state from - public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, IEnumerable baseBuffers = null) - { - _size = (int)parent.Size; - _systemMemoryType = context.Capabilities.MemoryType; - - // Backend managed is always auto, unified memory is always host. - _desiredType = BufferBackingType.HostMemory; - _canSwap = _systemMemoryType != SystemMemoryType.BackendManaged && _systemMemoryType != SystemMemoryType.UnifiedMemory; - - if (_canSwap) - { - // Might want to start certain buffers as being device local, - // and the usage might also lock those buffers into being device local. - - BufferStage storageFlags = stage & BufferStage.StorageMask; - - if (parent.Size > DeviceLocalSizeThreshold && baseBuffers == null) - { - _desiredType = BufferBackingType.DeviceMemory; - } - - if (storageFlags != 0) - { - // Storage buffer bindings may require special treatment. - - var rawStage = stage & BufferStage.StageMask; - - if (rawStage == BufferStage.Fragment) - { - // Fragment read should start device local. - - _desiredType = BufferBackingType.DeviceMemory; - - if (storageFlags != BufferStage.StorageRead) - { - // Fragment write should stay device local until the use doesn't happen anymore. - - _deviceLocalForceCount = DeviceLocalForceExpiry; - } - } - - // TODO: Might be nice to force atomic access to be device local for any stage. - } - - if (baseBuffers != null) - { - foreach (Buffer buffer in baseBuffers) - { - CombineState(buffer.BackingState); - } - } - } - } - - /// - /// Combine buffer backing types, selecting the one with highest priority. - /// - /// First buffer backing type - /// Second buffer backing type - /// Combined buffer backing type - private static BufferBackingType CombineTypes(BufferBackingType left, BufferBackingType right) - { - return (BufferBackingType)Math.Max((int)left, (int)right); - } - - /// - /// Combine the state from the given buffer backing state with this one, - /// so that the state isn't lost when migrating buffers. - /// - /// Buffer state to combine into this state - private void CombineState(BufferBackingState oldState) - { - _setCount += oldState._setCount; - _writeCount += oldState._writeCount; - _flushCount += oldState._flushCount; - _flushTemp += oldState._flushTemp; - _lastFlushWrite = -1; - _deviceLocalForceCount = Math.Max(_deviceLocalForceCount, oldState._deviceLocalForceCount); - - _canSwap &= oldState._canSwap; - - _desiredType = CombineTypes(_desiredType, oldState._desiredType); - } - - /// - /// Get the buffer access for the desired backing type, and record that type as now being active. - /// - /// Parent buffer - /// Buffer access - public BufferAccess SwitchAccess(Buffer parent) - { - BufferAccess access = parent.SparseCompatible ? BufferAccess.SparseCompatible : BufferAccess.Default; - - bool isBackendManaged = _systemMemoryType == SystemMemoryType.BackendManaged; - - if (!isBackendManaged) - { - switch (_desiredType) - { - case BufferBackingType.HostMemory: - access |= BufferAccess.HostMemory; - break; - case BufferBackingType.DeviceMemory: - access |= BufferAccess.DeviceMemory; - break; - case BufferBackingType.DeviceMemoryWithFlush: - access |= BufferAccess.DeviceMemoryMapped; - break; - } - } - - _activeType = _desiredType; - - return access; - } - - /// - /// Record when data has been uploaded to the buffer. - /// - public void RecordSet() - { - _setCount++; - - ConsiderUseCounts(); - } - - /// - /// Record when data has been flushed from the buffer. - /// - public void RecordFlush() - { - if (_lastFlushWrite != _writeCount) - { - // If it's on the same page as the last flush, ignore it. - _lastFlushWrite = _writeCount; - _flushCount++; - } - } - - /// - /// Determine if the buffer backing should be changed. - /// - /// True if the desired backing type is different from the current type - public readonly bool ShouldChangeBacking() - { - return _desiredType != _activeType; - } - - /// - /// Determine if the buffer backing should be changed, considering a new use with the given buffer stage. - /// - /// Buffer stage for the use - /// True if the desired backing type is different from the current type - public bool ShouldChangeBacking(BufferStage stage) - { - if (!_canSwap) - { - return false; - } - - BufferStage storageFlags = stage & BufferStage.StorageMask; - - if (storageFlags != 0) - { - if (storageFlags != BufferStage.StorageRead) - { - // Storage write. - _writeCount++; - - var rawStage = stage & BufferStage.StageMask; - - if (rawStage == BufferStage.Fragment) - { - // Switch to device memory, swap back only if this use disappears. - - _desiredType = CombineTypes(_desiredType, BufferBackingType.DeviceMemory); - _deviceLocalForceCount = DeviceLocalForceExpiry; - - // TODO: Might be nice to force atomic access to be device local for any stage. - } - } - - ConsiderUseCounts(); - } - - return _desiredType != _activeType; - } - - /// - /// Evaluate the current counts to determine what the buffer's desired backing type is. - /// This method depends on heuristics devised by testing a variety of software. - /// - private void ConsiderUseCounts() - { - if (_canSwap) - { - if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold) - { - if (_deviceLocalForceCount > 0 && --_deviceLocalForceCount != 0) - { - // Some buffer usage demanded that the buffer stay device local. - // The desired type was selected when this counter was set. - } - else if (_flushCount > 0 || _flushTemp-- > 0) - { - // Buffers that flush should ideally be mapped in host address space for easy copies. - // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). - // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. - _desiredType = _size > DeviceLocalSizeThreshold ? BufferBackingType.DeviceMemoryWithFlush : BufferBackingType.HostMemory; - } - else if (_writeCount >= WriteCountThreshold) - { - // Buffers that are written often should ideally be in the device local heap. (Storage buffers) - _desiredType = BufferBackingType.DeviceMemory; - } - else if (_setCount > SetCountThreshold) - { - // Buffers that have their data set often should ideally be host mapped. (Constant buffers) - _desiredType = BufferBackingType.HostMemory; - } - - // It's harder for a buffer that is flushed to revert to another type of mapping. - if (_flushCount > 0) - { - _flushTemp = 1000; - } - - _lastFlushWrite = -1; - _flushCount = 0; - _writeCount = 0; - _setCount = 0; - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 66d2cdb62..c6284780d 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -107,9 +107,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// GPU memory manager where the buffer is mapped /// Start GPU virtual address of the buffer /// Size in bytes of the buffer - /// The type of usage that created the buffer /// Contiguous physical range of the buffer, after address translation - public MultiRange TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size, BufferStage stage) + public MultiRange TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size) { if (gpuVa == 0) { @@ -120,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (address != MemoryManager.PteUnmapped) { - CreateBuffer(address, size, stage); + CreateBuffer(address, size); } return new MultiRange(address, size); @@ -133,9 +132,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// GPU memory manager where the buffer is mapped /// Start GPU virtual address of the buffer /// Size in bytes of the buffer - /// The type of usage that created the buffer /// Physical ranges of the buffer, after address translation - public MultiRange TranslateAndCreateMultiBuffers(MemoryManager memoryManager, ulong gpuVa, ulong size, BufferStage stage) + public MultiRange TranslateAndCreateMultiBuffers(MemoryManager memoryManager, ulong gpuVa, ulong size) { if (gpuVa == 0) { @@ -151,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Memory return range; } - CreateBuffer(range, stage); + CreateBuffer(range); return range; } @@ -163,9 +161,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// GPU memory manager where the buffer is mapped /// Start GPU virtual address of the buffer /// Size in bytes of the buffer - /// The type of usage that created the buffer /// Physical ranges of the buffer, after address translation - public MultiRange TranslateAndCreateMultiBuffersPhysicalOnly(MemoryManager memoryManager, ulong gpuVa, ulong size, BufferStage stage) + public MultiRange TranslateAndCreateMultiBuffersPhysicalOnly(MemoryManager memoryManager, ulong gpuVa, ulong size) { if (gpuVa == 0) { @@ -189,11 +186,11 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (range.Count > 1) { - CreateBuffer(subRange.Address, subRange.Size, stage, SparseBufferAlignmentSize); + CreateBuffer(subRange.Address, subRange.Size, SparseBufferAlignmentSize); } else { - CreateBuffer(subRange.Address, subRange.Size, stage); + CreateBuffer(subRange.Address, subRange.Size); } } } @@ -206,12 +203,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// This can be used to ensure the existance of a buffer. /// /// Physical ranges of memory where the buffer data is located - /// The type of usage that created the buffer - public void CreateBuffer(MultiRange range, BufferStage stage) + public void CreateBuffer(MultiRange range) { if (range.Count > 1) { - CreateMultiRangeBuffer(range, stage); + CreateMultiRangeBuffer(range); } else { @@ -219,7 +215,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (subRange.Address != MemoryManager.PteUnmapped) { - CreateBuffer(subRange.Address, subRange.Size, stage); + CreateBuffer(subRange.Address, subRange.Size); } } } @@ -230,8 +226,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in memory /// Size of the buffer in bytes - /// The type of usage that created the buffer - public void CreateBuffer(ulong address, ulong size, BufferStage stage) + public void CreateBuffer(ulong address, ulong size) { ulong endAddress = address + size; @@ -244,7 +239,7 @@ namespace Ryujinx.Graphics.Gpu.Memory alignedEndAddress += BufferAlignmentSize; } - CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress, stage); + CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress); } /// @@ -253,9 +248,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in memory /// Size of the buffer in bytes - /// The type of usage that created the buffer /// Alignment of the start address of the buffer in bytes - public void CreateBuffer(ulong address, ulong size, BufferStage stage, ulong alignment) + public void CreateBuffer(ulong address, ulong size, ulong alignment) { ulong alignmentMask = alignment - 1; ulong pageAlignmentMask = BufferAlignmentMask; @@ -270,7 +264,7 @@ namespace Ryujinx.Graphics.Gpu.Memory alignedEndAddress += pageAlignmentMask; } - CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress, stage, alignment); + CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress, alignment); } /// @@ -278,8 +272,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// if it does not exist yet. /// /// Physical ranges of memory - /// The type of usage that created the buffer - private void CreateMultiRangeBuffer(MultiRange range, BufferStage stage) + private void CreateMultiRangeBuffer(MultiRange range) { // Ensure all non-contiguous buffer we might use are sparse aligned. for (int i = 0; i < range.Count; i++) @@ -288,7 +281,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (subRange.Address != MemoryManager.PteUnmapped) { - CreateBuffer(subRange.Address, subRange.Size, stage, SparseBufferAlignmentSize); + CreateBuffer(subRange.Address, subRange.Size, SparseBufferAlignmentSize); } } @@ -438,9 +431,9 @@ namespace Ryujinx.Graphics.Gpu.Memory result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence) { - MultiRange range = TranslateAndCreateBuffer(memoryManager, gpuVa, size, BufferStage.Internal); + MultiRange range = TranslateAndCreateBuffer(memoryManager, gpuVa, size); ulong address = range.GetSubRange(0).Address; - result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size, BufferStage.Internal)); + result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size)); _dirtyCache[gpuVa] = result; } @@ -473,9 +466,9 @@ namespace Ryujinx.Graphics.Gpu.Memory result.EndGpuAddress < alignedEndGpuVa || result.UnmappedSequence != result.Buffer.UnmappedSequence) { - MultiRange range = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size, BufferStage.None); + MultiRange range = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size); ulong address = range.GetSubRange(0).Address; - result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size, BufferStage.None)); + result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size)); _modifiedCache[alignedGpuVa] = result; } @@ -492,8 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in guest memory /// Size in bytes of the buffer - /// The type of usage that created the buffer - private void CreateBufferAligned(ulong address, ulong size, BufferStage stage) + private void CreateBufferAligned(ulong address, ulong size) { Buffer[] overlaps = _bufferOverlaps; int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps); @@ -554,13 +546,13 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong newSize = endAddress - address; - CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps, overlapsCount); + CreateBufferAligned(address, newSize, anySparseCompatible, overlaps, overlapsCount); } } else { // No overlap, just create a new buffer. - Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false); + Buffer buffer = new(_context, _physicalMemory, address, size, sparseCompatible: false); lock (_buffers) { @@ -578,9 +570,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in guest memory /// Size in bytes of the buffer - /// The type of usage that created the buffer /// Alignment of the start address of the buffer - private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment) + private void CreateBufferAligned(ulong address, ulong size, ulong alignment) { Buffer[] overlaps = _bufferOverlaps; int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps); @@ -633,13 +624,13 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong newSize = endAddress - address; - CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps, overlapsCount); + CreateBufferAligned(address, newSize, sparseAligned, overlaps, overlapsCount); } } else { // No overlap, just create a new buffer. - Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseAligned); + Buffer buffer = new(_context, _physicalMemory, address, size, sparseAligned); lock (_buffers) { @@ -657,13 +648,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in guest memory /// Size in bytes of the buffer - /// The type of usage that created the buffer /// Indicates if the buffer can be used in a sparse buffer mapping /// Buffers overlapping the range /// Total of overlaps - private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, Buffer[] overlaps, int overlapsCount) + private void CreateBufferAligned(ulong address, ulong size, bool sparseCompatible, Buffer[] overlaps, int overlapsCount) { - Buffer newBuffer = new Buffer(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps.Take(overlapsCount)); + Buffer newBuffer = new Buffer(_context, _physicalMemory, address, size, sparseCompatible, overlaps.Take(overlapsCount)); lock (_buffers) { @@ -714,7 +704,7 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int index = 0; index < overlapCount; index++) { - CreateMultiRangeBuffer(overlaps[index].Range, BufferStage.None); + CreateMultiRangeBuffer(overlaps[index].Range); } } @@ -741,8 +731,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the copy public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size) { - MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy); - MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy); + MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size); + MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size); if (srcRange.Count == 1 && dstRange.Count == 1) { @@ -798,8 +788,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the copy private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size) { - Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy); - Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy); + Buffer srcBuffer = GetBuffer(srcAddress, size); + Buffer dstBuffer = GetBuffer(dstAddress, size); int srcOffset = (int)(srcAddress - srcBuffer.Address); int dstOffset = (int)(dstAddress - dstBuffer.Address); @@ -813,7 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (srcBuffer.IsModified(srcAddress, size)) { - dstBuffer.SignalModified(dstAddress, size, BufferStage.Copy); + dstBuffer.SignalModified(dstAddress, size); } else { @@ -838,12 +828,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Value to be written into the buffer public void ClearBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size, uint value) { - MultiRange range = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, gpuVa, size, BufferStage.Copy); + MultiRange range = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, gpuVa, size); for (int index = 0; index < range.Count; index++) { MemoryRange subRange = range.GetSubRange(index); - Buffer buffer = GetBuffer(subRange.Address, subRange.Size, BufferStage.Copy); + Buffer buffer = GetBuffer(subRange.Address, subRange.Size); int offset = (int)(subRange.Address - buffer.Address); @@ -859,19 +849,18 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Gets a buffer sub-range starting at a given memory address, aligned to the next page boundary. /// /// Physical regions of memory where the buffer is mapped - /// Buffer stage that triggered the access /// Whether the buffer will be written to by this use /// The buffer sub-range starting at the given memory address - public BufferRange GetBufferRangeAligned(MultiRange range, BufferStage stage, bool write = false) + public BufferRange GetBufferRangeAligned(MultiRange range, bool write = false) { if (range.Count > 1) { - return GetBuffer(range, stage, write).GetRange(range); + return GetBuffer(range, write).GetRange(range); } else { MemoryRange subRange = range.GetSubRange(0); - return GetBuffer(subRange.Address, subRange.Size, stage, write).GetRangeAligned(subRange.Address, subRange.Size, write); + return GetBuffer(subRange.Address, subRange.Size, write).GetRangeAligned(subRange.Address, subRange.Size, write); } } @@ -879,19 +868,18 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Gets a buffer sub-range for a given memory range. /// /// Physical regions of memory where the buffer is mapped - /// Buffer stage that triggered the access /// Whether the buffer will be written to by this use /// The buffer sub-range for the given range - public BufferRange GetBufferRange(MultiRange range, BufferStage stage, bool write = false) + public BufferRange GetBufferRange(MultiRange range, bool write = false) { if (range.Count > 1) { - return GetBuffer(range, stage, write).GetRange(range); + return GetBuffer(range, write).GetRange(range); } else { MemoryRange subRange = range.GetSubRange(0); - return GetBuffer(subRange.Address, subRange.Size, stage, write).GetRange(subRange.Address, subRange.Size, write); + return GetBuffer(subRange.Address, subRange.Size, write).GetRange(subRange.Address, subRange.Size, write); } } @@ -900,10 +888,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// A buffer overlapping with the specified range is assumed to already exist on the cache. /// /// Physical regions of memory where the buffer is mapped - /// Buffer stage that triggered the access /// Whether the buffer will be written to by this use /// The buffer where the range is fully contained - private MultiRangeBuffer GetBuffer(MultiRange range, BufferStage stage, bool write = false) + private MultiRangeBuffer GetBuffer(MultiRange range, bool write = false) { for (int i = 0; i < range.Count; i++) { @@ -915,7 +902,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (write) { - subBuffer.SignalModified(subRange.Address, subRange.Size, stage); + subBuffer.SignalModified(subRange.Address, subRange.Size); } } @@ -948,10 +935,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Start address of the memory range /// Size in bytes of the memory range - /// Buffer stage that triggered the access /// Whether the buffer will be written to by this use /// The buffer where the range is fully contained - private Buffer GetBuffer(ulong address, ulong size, BufferStage stage, bool write = false) + private Buffer GetBuffer(ulong address, ulong size, bool write = false) { Buffer buffer; @@ -964,7 +950,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (write) { - buffer.SignalModified(address, size, stage); + buffer.SignalModified(address, size); } } else @@ -1018,18 +1004,6 @@ namespace Ryujinx.Graphics.Gpu.Memory } } - /// - /// Signal that the given buffer's handle has changed, - /// forcing rebind and any overlapping multi-range buffers to be recreated. - /// - /// The buffer that has changed handle - public void BufferBackingChanged(Buffer buffer) - { - NotifyBuffersModified?.Invoke(); - - RecreateMultiRangeBuffers(buffer.Address, buffer.Size); - } - /// /// Prune any invalid entries from a quick access dictionary. /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 409867e09..8f2201e0a 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Type of each index buffer element public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); _indexBuffer.Range = range; _indexBuffer.Type = type; @@ -186,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Vertex divisor of the buffer, for instanced draws public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); _vertexBuffers[index].Range = range; _vertexBuffers[index].Stride = stride; @@ -213,7 +213,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the transform feedback buffer public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size); _transformFeedbackBuffers[index] = new BufferBounds(range); _transformFeedbackBuffersDirty = true; @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Memory gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags)); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size); _cpStorageBuffers.SetBounds(index, range, flags); } @@ -284,7 +284,7 @@ namespace Ryujinx.Graphics.Gpu.Memory gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags)); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size); if (!buffers.Buffers[index].Range.Equals(range)) { @@ -303,7 +303,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the storage buffer public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); _cpUniformBuffers.SetBounds(index, range); } @@ -318,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the storage buffer public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage)); + MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); _gpUniformBuffers[stage].SetBounds(index, range); _gpUniformBuffersDirty = true; @@ -502,14 +502,14 @@ namespace Ryujinx.Graphics.Gpu.Memory foreach (var binding in _bufferTextures) { var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); - var range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore); + var range = bufferCache.GetBufferRange(binding.Range, isStore); binding.Texture.SetStorage(range); // The texture must be rebound to use the new storage if it was updated. if (binding.IsImage) { - _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture); + _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format); } else { @@ -526,7 +526,7 @@ namespace Ryujinx.Graphics.Gpu.Memory foreach (var binding in _bufferTextureArrays) { - var range = bufferCache.GetBufferRange(binding.Range, BufferStage.None); + var range = bufferCache.GetBufferRange(binding.Range); binding.Texture.SetStorage(range); textureArray[0] = binding.Texture; @@ -536,7 +536,7 @@ namespace Ryujinx.Graphics.Gpu.Memory foreach (var binding in _bufferImageArrays) { var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); - var range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore); + var range = bufferCache.GetBufferRange(binding.Range, isStore); binding.Texture.SetStorage(range); textureArray[0] = binding.Texture; @@ -565,7 +565,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (!_indexBuffer.Range.IsUnmapped) { - BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer); + BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); } @@ -597,7 +597,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer); + BufferRange buffer = bufferCache.GetBufferRange(vb.Range); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); } @@ -637,7 +637,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true); + tfbs[index] = bufferCache.GetBufferRange(tfb.Range, write: true); } _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); @@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset); - buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true)); + buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, write: true)); } } @@ -751,7 +751,6 @@ namespace Ryujinx.Graphics.Gpu.Memory for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) { ref var buffers = ref bindings[(int)stage - 1]; - BufferStage bufferStage = BufferStageUtils.FromShaderStage(stage); for (int index = 0; index < buffers.Count; index++) { @@ -763,8 +762,8 @@ namespace Ryujinx.Graphics.Gpu.Memory { var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var range = isStorage - ? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) - : bufferCache.GetBufferRange(bounds.Range, bufferStage); + ? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite) + : bufferCache.GetBufferRange(bounds.Range); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } @@ -800,8 +799,8 @@ namespace Ryujinx.Graphics.Gpu.Memory { var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var range = isStorage - ? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) - : bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); + ? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite) + : bufferCache.GetBufferRange(bounds.Range); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } @@ -873,17 +872,17 @@ namespace Ryujinx.Graphics.Gpu.Memory ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, + Format format, bool isImage) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range); - _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage)); + _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage)); } /// /// Sets the buffer storage of a buffer texture array element. This will be bound when the buffer manager commits bindings. /// - /// Shader stage accessing the texture /// Texture array where the element will be inserted /// Buffer texture /// Physical ranges of memory where the buffer texture data is located @@ -891,22 +890,21 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Index of the binding on the array /// Format of the buffer texture public void SetBufferTextureStorage( - ShaderStage stage, ITextureArray array, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index) + int index, + Format format) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range); - _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); + _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index, format)); } /// /// Sets the buffer storage of a buffer image array element. This will be bound when the buffer manager commits bindings. /// - /// Shader stage accessing the texture /// Image array where the element will be inserted /// Buffer texture /// Physical ranges of memory where the buffer texture data is located @@ -914,16 +912,16 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Index of the binding on the array /// Format of the buffer texture public void SetBufferTextureStorage( - ShaderStage stage, IImageArray array, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index) + int index, + Format format) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range); - _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); + _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index, format)); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs index ce9985318..0a5268031 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs @@ -1,21 +1,37 @@ using System; -using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory { /// - /// A record of when buffer data was copied from multiple buffers to one migration target, - /// along with the SyncNumber when the migration will be complete. - /// Keeps the source buffers alive for data flushes until the migration is complete. - /// All spans cover the full range of the "destination" buffer. + /// A record of when buffer data was copied from one buffer to another, along with the SyncNumber when the migration will be complete. + /// Keeps the source buffer alive for data flushes until the migration is complete. /// internal class BufferMigration : IDisposable { /// - /// Ranges from source buffers that were copied as part of this migration. - /// Ordered by increasing base address. + /// The offset for the migrated region. /// - public BufferMigrationSpan[] Spans { get; private set; } + private readonly ulong _offset; + + /// + /// The size for the migrated region. + /// + private readonly ulong _size; + + /// + /// The buffer that was migrated from. + /// + private readonly Buffer _buffer; + + /// + /// The source range action, to be called on overlap with an unreached sync number. + /// + private readonly Action _sourceRangeAction; + + /// + /// The source range list. + /// + private readonly BufferModifiedRangeList _source; /// /// The destination range list. This range list must be updated when flushing the source. @@ -27,193 +43,55 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public readonly ulong SyncNumber; - /// - /// Number of active users there are traversing this migration's spans. - /// - private int _refCount; - - /// - /// Create a new buffer migration. - /// - /// Source spans for the migration - /// Destination buffer range list - /// Sync number where this migration will be complete - public BufferMigration(BufferMigrationSpan[] spans, BufferModifiedRangeList destination, ulong syncNumber) - { - Spans = spans; - Destination = destination; - SyncNumber = syncNumber; - } - - /// - /// Add a span to the migration. Allocates a new array with the target size, and replaces it. - /// - /// - /// The base address for the span is assumed to be higher than all other spans in the migration, - /// to keep the span array ordered. - /// - public void AddSpanToEnd(BufferMigrationSpan span) - { - BufferMigrationSpan[] oldSpans = Spans; - - BufferMigrationSpan[] newSpans = new BufferMigrationSpan[oldSpans.Length + 1]; - - oldSpans.CopyTo(newSpans, 0); - - newSpans[oldSpans.Length] = span; - - Spans = newSpans; - } - - /// - /// Performs the given range action, or one from a migration that overlaps and has not synced yet. - /// - /// The offset to pass to the action - /// The size to pass to the action - /// The sync number that has been reached - /// The action to perform - public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferFlushAction rangeAction) - { - long syncDiff = (long)(syncNumber - SyncNumber); - - if (syncDiff >= 0) - { - // The migration has completed. Run the parent action. - rangeAction(offset, size, syncNumber); - } - else - { - Interlocked.Increment(ref _refCount); - - ulong prevAddress = offset; - ulong endAddress = offset + size; - - foreach (BufferMigrationSpan span in Spans) - { - if (!span.Overlaps(offset, size)) - { - continue; - } - - if (span.Address > prevAddress) - { - // There's a gap between this span and the last (or the start address). Flush the range using the parent action. - - rangeAction(prevAddress, span.Address - prevAddress, syncNumber); - } - - span.RangeActionWithMigration(offset, size, syncNumber); - - prevAddress = span.Address + span.Size; - } - - if (endAddress > prevAddress) - { - // There's a gap at the end of the range with no migration. Flush the range using the parent action. - rangeAction(prevAddress, endAddress - prevAddress, syncNumber); - } - - Interlocked.Decrement(ref _refCount); - } - } - - /// - /// Dispose the buffer migration. This removes the reference from the destination range list, - /// and runs all the dispose buffers for the migration spans. (typically disposes the source buffer) - /// - public void Dispose() - { - while (Volatile.Read(ref _refCount) > 0) - { - // Coming into this method, the sync for the migration will be met, so nothing can increment the ref count. - // However, an existing traversal of the spans for data flush could still be in progress. - // Spin if this is ever the case, so they don't get disposed before the operation is complete. - } - - Destination.RemoveMigration(this); - - foreach (BufferMigrationSpan span in Spans) - { - span.Dispose(); - } - } - } - - /// - /// A record of when buffer data was copied from one buffer to another, for a specific range in a source buffer. - /// Keeps the source buffer alive for data flushes until the migration is complete. - /// - internal readonly struct BufferMigrationSpan : IDisposable - { - /// - /// The offset for the migrated region. - /// - public readonly ulong Address; - - /// - /// The size for the migrated region. - /// - public readonly ulong Size; - - /// - /// The action to perform when the migration isn't needed anymore. - /// - private readonly Action _disposeAction; - - /// - /// The source range action, to be called on overlap with an unreached sync number. - /// - private readonly BufferFlushAction _sourceRangeAction; - - /// - /// Optional migration for the source data. Can chain together if many migrations happen in a short time. - /// If this is null, then _sourceRangeAction will always provide up to date data. - /// - private readonly BufferMigration _source; - /// /// Creates a record for a buffer migration. /// /// The source buffer for this migration - /// The action to perform when the migration isn't needed anymore /// The flush action for the source buffer - /// Pending migration for the source buffer - public BufferMigrationSpan( + /// The modified range list for the source buffer + /// The modified range list for the destination buffer + /// The sync number for when the migration is complete + public BufferMigration( Buffer buffer, - Action disposeAction, - BufferFlushAction sourceRangeAction, - BufferMigration source) + Action sourceRangeAction, + BufferModifiedRangeList source, + BufferModifiedRangeList dest, + ulong syncNumber) { - Address = buffer.Address; - Size = buffer.Size; - _disposeAction = disposeAction; + _offset = buffer.Address; + _size = buffer.Size; + _buffer = buffer; _sourceRangeAction = sourceRangeAction; _source = source; + Destination = dest; + SyncNumber = syncNumber; } - /// - /// Creates a record for a buffer migration, using the default buffer dispose action. - /// - /// The source buffer for this migration - /// The flush action for the source buffer - /// Pending migration for the source buffer - public BufferMigrationSpan( - Buffer buffer, - BufferFlushAction sourceRangeAction, - BufferMigration source) : this(buffer, buffer.DecrementReferenceCount, sourceRangeAction, source) { } - /// /// Determine if the given range overlaps this migration, and has not been completed yet. /// /// Start offset /// Range size + /// The sync number that was waited on /// True if overlapping and in progress, false otherwise - public bool Overlaps(ulong offset, ulong size) + public bool Overlaps(ulong offset, ulong size, ulong syncNumber) { ulong end = offset + size; - ulong destEnd = Address + Size; + ulong destEnd = _offset + _size; + long syncDiff = (long)(syncNumber - SyncNumber); // syncNumber is less if the copy has not completed. - return !(end <= Address || offset >= destEnd); + return !(end <= _offset || offset >= destEnd) && syncDiff < 0; + } + + /// + /// Determine if the given range matches this migration. + /// + /// Start offset + /// Range size + /// True if the range exactly matches, false otherwise + public bool FullyMatches(ulong offset, ulong size) + { + return _offset == offset && _size == size; } /// @@ -222,30 +100,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Start offset /// Range size /// Current sync number - public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber) + /// The modified range list that originally owned this range + public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent) { ulong end = offset + size; - end = Math.Min(Address + Size, end); - offset = Math.Max(Address, offset); + end = Math.Min(_offset + _size, end); + offset = Math.Max(_offset, offset); size = end - offset; - if (_source != null) - { - _source.RangeActionWithMigration(offset, size, syncNumber, _sourceRangeAction); - } - else - { - _sourceRangeAction(offset, size, syncNumber); - } + _source.RangeActionWithMigration(offset, size, syncNumber, parent, _sourceRangeAction); } /// - /// Removes this migration span, potentially allowing for the source buffer to be disposed. + /// Removes this reference to the range list, potentially allowing for the source buffer to be disposed. /// public void Dispose() { - _disposeAction(); + Destination.RemoveMigration(this); + + _buffer.DecrementReferenceCount(); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index d330de638..6ada8a4b2 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Pools; using Ryujinx.Memory.Range; using System; +using System.Collections.Generic; using System.Linq; namespace Ryujinx.Graphics.Gpu.Memory @@ -71,10 +72,10 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly GpuContext _context; private readonly Buffer _parent; - private readonly BufferFlushAction _flushAction; + private readonly Action _flushAction; - private BufferMigration _source; - private BufferModifiedRangeList _migrationTarget; + private List _sources; + private BufferMigration _migrationTarget; private readonly object _lock = new(); @@ -98,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// GPU context that the buffer range list belongs to /// The parent buffer that owns this range list /// The flush action for the parent buffer - public BufferModifiedRangeList(GpuContext context, Buffer parent, BufferFlushAction flushAction) : base(BackingInitialSize) + public BufferModifiedRangeList(GpuContext context, Buffer parent, Action flushAction) : base(BackingInitialSize) { _context = context; _parent = parent; @@ -198,36 +199,6 @@ namespace Ryujinx.Graphics.Gpu.Memory } } - /// - /// Gets modified ranges within the specified region, and then fires the given action for each range individually. - /// - /// Start address to query - /// Size to query - /// Sync number required for a range to be signalled - /// The action to call for each modified range - public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action rangeAction) - { - int count = 0; - - ref var overlaps = ref ThreadStaticArray.Get(); - - // Range list must be consistent for this operation. - lock (_lock) - { - count = FindOverlapsNonOverlapping(address, size, ref overlaps); - } - - for (int i = 0; i < count; i++) - { - BufferModifiedRange overlap = overlaps[i]; - - if (overlap.SyncNumber == syncNumber) - { - rangeAction(overlap.Address, overlap.Size); - } - } - } - /// /// Gets modified ranges within the specified region, and then fires the given action for each range individually. /// @@ -274,16 +245,41 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The offset to pass to the action /// The size to pass to the action /// The sync number that has been reached + /// The modified range list that originally owned this range /// The action to perform - public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferFlushAction rangeAction) + public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent, Action rangeAction) { - if (_source != null) + bool firstSource = true; + + if (parent != this) { - _source.RangeActionWithMigration(offset, size, syncNumber, rangeAction); + lock (_lock) + { + if (_sources != null) + { + foreach (BufferMigration source in _sources) + { + if (source.Overlaps(offset, size, syncNumber)) + { + if (firstSource && !source.FullyMatches(offset, size)) + { + // Perform this buffer's action first. The migrations will run after. + rangeAction(offset, size); + } + + source.RangeActionWithMigration(offset, size, syncNumber, parent); + + firstSource = false; + } + } + } + } } - else + + if (firstSource) { - rangeAction(offset, size, syncNumber); + // No overlapping migrations, or they are not meant for this range, flush the data using the given action. + rangeAction(offset, size); } } @@ -323,7 +319,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ClearPart(overlap, clampAddress, clampEnd); - RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction); + RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, overlap.Parent, _flushAction); } } @@ -333,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Memory // There is a migration target to call instead. This can't be changed after set so accessing it outside the lock is fine. - _migrationTarget.RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress); + _migrationTarget.Destination.RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress); } /// @@ -371,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (rangeCount == -1) { - _migrationTarget.WaitForAndFlushRanges(address, size); + _migrationTarget.Destination.WaitForAndFlushRanges(address, size); return; } @@ -411,9 +407,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Inherit ranges from another modified range list. /// - /// - /// Assumes that ranges will be inherited in address ascending order. - /// /// The range list to inherit from /// The action to call for each modified range public void InheritRanges(BufferModifiedRangeList ranges, Action registerRangeAction) @@ -422,31 +415,18 @@ namespace Ryujinx.Graphics.Gpu.Memory lock (ranges._lock) { + BufferMigration migration = new(ranges._parent, ranges._flushAction, ranges, this, _context.SyncNumber); + + ranges._parent.IncrementReferenceCount(); + ranges._migrationTarget = migration; + + _context.RegisterBufferMigration(migration); + inheritRanges = ranges.ToArray(); lock (_lock) { - // Copy over the migration from the previous range list - - BufferMigration oldMigration = ranges._source; - - BufferMigrationSpan span = new BufferMigrationSpan(ranges._parent, ranges._flushAction, oldMigration); - ranges._parent.IncrementReferenceCount(); - - if (_source == null) - { - // Create a new migration. - _source = new BufferMigration(new BufferMigrationSpan[] { span }, this, _context.SyncNumber); - - _context.RegisterBufferMigration(_source); - } - else - { - // Extend the migration - _source.AddSpanToEnd(span); - } - - ranges._migrationTarget = this; + (_sources ??= new List()).Add(migration); foreach (BufferModifiedRange range in inheritRanges) { @@ -465,27 +445,6 @@ namespace Ryujinx.Graphics.Gpu.Memory } } - /// - /// Register a migration from previous buffer storage. This migration is from a snapshot of the buffer's - /// current handle to its handle in the future, and is assumed to be complete when the sync action completes. - /// When the migration completes, the handle is disposed. - /// - public void SelfMigration() - { - lock (_lock) - { - BufferMigrationSpan span = new(_parent, _parent.GetSnapshotDisposeAction(), _parent.GetSnapshotFlushAction(), _source); - BufferMigration migration = new(new BufferMigrationSpan[] { span }, this, _context.SyncNumber); - - // Migration target is used to redirect flush actions to the latest range list, - // so we don't need to set it here. (this range list is still the latest) - - _context.RegisterBufferMigration(migration); - - _source = migration; - } - } - /// /// Removes a source buffer migration, indicating its copy has completed. /// @@ -494,10 +453,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { lock (_lock) { - if (_source == migration) - { - _source = null; - } + _sources.Remove(migration); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferPreFlush.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferPreFlush.cs deleted file mode 100644 index d58b9ea66..000000000 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferPreFlush.cs +++ /dev/null @@ -1,295 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.Gpu.Memory -{ - /// - /// Manages flushing ranges from buffers in advance for easy access, if they are flushed often. - /// Typically, from device local memory to a host mapped target for cached access. - /// - internal class BufferPreFlush : IDisposable - { - private const ulong PageSize = MemoryManager.PageSize; - - /// - /// Threshold for the number of copies without a flush required to disable preflush on a page. - /// - private const int DeactivateCopyThreshold = 200; - - /// - /// Value that indicates whether a page has been flushed or copied before. - /// - private enum PreFlushState - { - None, - HasFlushed, - HasCopied - } - - /// - /// Flush state for each page of the buffer. - /// Controls whether data should be copied to the flush buffer, what sync is expected - /// and unflushed copy counting for stopping copies that are no longer needed. - /// - private struct PreFlushPage - { - public PreFlushState State; - public ulong FirstActivatedSync; - public ulong LastCopiedSync; - public int CopyCount; - } - - /// - /// True if there are ranges that should copy to the flush buffer, false otherwise. - /// - public bool ShouldCopy { get; private set; } - - private readonly GpuContext _context; - private readonly Buffer _buffer; - private readonly PreFlushPage[] _pages; - private readonly ulong _address; - private readonly ulong _size; - private readonly ulong _misalignment; - private readonly Action _flushAction; - - private BufferHandle _flushBuffer; - - public BufferPreFlush(GpuContext context, Buffer parent, Action flushAction) - { - _context = context; - _buffer = parent; - _address = parent.Address; - _size = parent.Size; - _pages = new PreFlushPage[BitUtils.DivRoundUp(_size, PageSize)]; - _misalignment = _address & (PageSize - 1); - - _flushAction = flushAction; - } - - /// - /// Ensure that the flush buffer exists. - /// - private void EnsureFlushBuffer() - { - if (_flushBuffer == BufferHandle.Null) - { - _flushBuffer = _context.Renderer.CreateBuffer((int)_size, BufferAccess.HostMemory); - } - } - - /// - /// Gets a page range from an address and size byte range. - /// - /// Range address - /// Range size - /// A page index and count - private (int index, int count) GetPageRange(ulong address, ulong size) - { - ulong offset = address - _address; - ulong endOffset = offset + size; - - int basePage = (int)(offset / PageSize); - int endPage = (int)((endOffset - 1) / PageSize); - - return (basePage, 1 + endPage - basePage); - } - - /// - /// Gets an offset and size range in the parent buffer from a page index and count. - /// - /// Range start page - /// Range page count - /// Offset and size range - private (int offset, int size) GetOffset(int startPage, int count) - { - int offset = (int)((ulong)startPage * PageSize - _misalignment); - int endOffset = (int)((ulong)(startPage + count) * PageSize - _misalignment); - - offset = Math.Max(0, offset); - endOffset = Math.Min((int)_size, endOffset); - - return (offset, endOffset - offset); - } - - /// - /// Copy a range of pages from the parent buffer into the flush buffer. - /// - /// Range start page - /// Range page count - private void CopyPageRange(int startPage, int count) - { - (int offset, int size) = GetOffset(startPage, count); - - EnsureFlushBuffer(); - - _context.Renderer.Pipeline.CopyBuffer(_buffer.Handle, _flushBuffer, offset, offset, size); - } - - /// - /// Copy a modified range into the flush buffer if it's marked as flushed. - /// Any pages the range overlaps are copied, and copies aren't repeated in the same sync number. - /// - /// Range address - /// Range size - public void CopyModified(ulong address, ulong size) - { - (int baseIndex, int count) = GetPageRange(address, size); - ulong syncNumber = _context.SyncNumber; - - int startPage = -1; - - for (int i = 0; i < count; i++) - { - int pageIndex = baseIndex + i; - ref PreFlushPage page = ref _pages[pageIndex]; - - if (page.State > PreFlushState.None) - { - // Perform the copy, and update the state of each page. - if (startPage == -1) - { - startPage = pageIndex; - } - - if (page.State != PreFlushState.HasCopied) - { - page.FirstActivatedSync = syncNumber; - page.State = PreFlushState.HasCopied; - } - else if (page.CopyCount++ >= DeactivateCopyThreshold) - { - page.CopyCount = 0; - page.State = PreFlushState.None; - } - - if (page.LastCopiedSync != syncNumber) - { - page.LastCopiedSync = syncNumber; - } - } - else if (startPage != -1) - { - CopyPageRange(startPage, pageIndex - startPage); - - startPage = -1; - } - } - - if (startPage != -1) - { - CopyPageRange(startPage, (baseIndex + count) - startPage); - } - } - - /// - /// Flush the given page range back into guest memory, optionally using data from the flush buffer. - /// The actual flushed range is an intersection of the page range and the address range. - /// - /// Address range start - /// Address range size - /// Page range start - /// Page range count - /// True if the data should come from the flush buffer - private void FlushPageRange(ulong address, ulong size, int startPage, int count, bool preFlush) - { - (int pageOffset, int pageSize) = GetOffset(startPage, count); - - int offset = (int)(address - _address); - int end = offset + (int)size; - - offset = Math.Max(offset, pageOffset); - end = Math.Min(end, pageOffset + pageSize); - - if (end >= offset) - { - BufferHandle handle = preFlush ? _flushBuffer : _buffer.Handle; - _flushAction(handle, _address + (ulong)offset, (ulong)(end - offset)); - } - } - - /// - /// Flush the given address range back into guest memory, optionally using data from the flush buffer. - /// When a copy has been performed on or before the waited sync number, the data can come from the flush buffer. - /// Otherwise, it flushes the parent buffer directly. - /// - /// Range address - /// Range size - /// Sync number that has been waited for - public void FlushWithAction(ulong address, ulong size, ulong syncNumber) - { - // Copy the parts of the range that have pre-flush copies that have been completed. - // Run the flush action for ranges that don't have pre-flush copies. - - // If a range doesn't have a pre-flush copy, consider adding one. - - (int baseIndex, int count) = GetPageRange(address, size); - - bool rangePreFlushed = false; - int startPage = -1; - - for (int i = 0; i < count; i++) - { - int pageIndex = baseIndex + i; - ref PreFlushPage page = ref _pages[pageIndex]; - - bool flushPage = false; - page.CopyCount = 0; - - if (page.State == PreFlushState.HasCopied) - { - if (syncNumber >= page.FirstActivatedSync) - { - // After the range is first activated, its data will always be copied to the preflush buffer on each sync. - flushPage = true; - } - } - else if (page.State == PreFlushState.None) - { - page.State = PreFlushState.HasFlushed; - ShouldCopy = true; - } - - if (flushPage) - { - if (!rangePreFlushed || startPage == -1) - { - if (startPage != -1) - { - FlushPageRange(address, size, startPage, pageIndex - startPage, false); - } - - rangePreFlushed = true; - startPage = pageIndex; - } - } - else if (rangePreFlushed || startPage == -1) - { - if (startPage != -1) - { - FlushPageRange(address, size, startPage, pageIndex - startPage, true); - } - - rangePreFlushed = false; - startPage = pageIndex; - } - } - - if (startPage != -1) - { - FlushPageRange(address, size, startPage, (baseIndex + count) - startPage, rangePreFlushed); - } - } - - /// - /// Dispose the flush buffer, if present. - /// - public void Dispose() - { - if (_flushBuffer != BufferHandle.Null) - { - _context.Renderer.DeleteBuffer(_flushBuffer); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs deleted file mode 100644 index d56abda28..000000000 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Ryujinx.Graphics.Shader; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Gpu.Memory -{ - /// - /// Pipeline stages that can modify buffer data, as well as flags indicating storage usage. - /// Must match ShaderStage for the shader stages, though anything after that can be in any order. - /// - internal enum BufferStage : byte - { - Compute, - Vertex, - TessellationControl, - TessellationEvaluation, - Geometry, - Fragment, - - Indirect, - VertexBuffer, - IndexBuffer, - Copy, - TransformFeedback, - Internal, - None, - - StageMask = 0x3f, - StorageMask = 0xc0, - - StorageRead = 0x40, - StorageWrite = 0x80, - -#pragma warning disable CA1069 // Enums values should not be duplicated - StorageAtomic = 0xc0 -#pragma warning restore CA1069 // Enums values should not be duplicated - } - - /// - /// Utility methods to convert shader stages and binding flags into buffer stages. - /// - internal static class BufferStageUtils - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage FromShaderStage(ShaderStage stage) - { - return (BufferStage)stage; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage FromShaderStage(int stageIndex) - { - return (BufferStage)(stageIndex + 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage FromUsage(BufferUsageFlags flags) - { - if (flags.HasFlag(BufferUsageFlags.Write)) - { - return BufferStage.StorageWrite; - } - else - { - return BufferStage.StorageRead; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage FromUsage(TextureUsageFlags flags) - { - if (flags.HasFlag(TextureUsageFlags.ImageStore)) - { - return BufferStage.StorageWrite; - } - else - { - return BufferStage.StorageRead; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage TextureBuffer(ShaderStage shaderStage, TextureUsageFlags flags) - { - return FromShaderStage(shaderStage) | FromUsage(flags); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage GraphicsStorage(int stageIndex, BufferUsageFlags flags) - { - return FromShaderStage(stageIndex) | FromUsage(flags); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferStage ComputeStorage(BufferUsageFlags flags) - { - return BufferStage.Compute | FromUsage(flags); - } - } -} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs index a5338fa55..fa79e4f92 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs @@ -34,26 +34,33 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public int Index { get; } + /// + /// The image format for the binding. + /// + public Format Format { get; } + /// /// Create a new buffer texture binding. /// - /// Array /// Buffer texture /// Physical ranges of memory where the buffer texture data is located /// Binding info /// Index of the binding on the array + /// Binding format public BufferTextureArrayBinding( T array, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index) + int index, + Format format) { Array = array; Texture = texture; Range = range; BindingInfo = bindingInfo; Index = index; + Format = format; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs index 1a3fde5b6..bf0beffa2 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs @@ -30,6 +30,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public TextureBindingInfo BindingInfo { get; } + /// + /// The image format for the binding. + /// + public Format Format { get; } + /// /// Whether the binding is for an image or a sampler. /// @@ -42,18 +47,21 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Buffer texture /// Physical ranges of memory where the buffer texture data is located /// Binding info + /// Binding format /// Whether the binding is for an image or a sampler public BufferTextureBinding( ShaderStage stage, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, + Format format, bool isImage) { Stage = stage; Texture = texture; Range = range; BindingInfo = bindingInfo; + Format = format; IsImage = isImage; } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index d1065431d..0b6c78fac 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Memory; -using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Memory; using Ryujinx.Memory.Range; using System; +using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -65,7 +65,6 @@ namespace Ryujinx.Graphics.Gpu.Memory MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler; - Physical.TextureCache.Initialize(); } /// @@ -243,9 +242,9 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(size); + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size); - ReadImpl(va, memoryOwner.Span, tracked); + GetSpan(va, size).CopyTo(memoryOwner.Memory.Span); return new WritableRegion(this, va, memoryOwner, tracked); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index b22cc01b8..4d09c3aab 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(checked((int)range.GetSize())); + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(range.GetSize()); - Span memorySpan = memoryOwner.Span; + Memory memory = memoryOwner.Memory; 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(memorySpan.Slice(offset, size)); + GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size)); } offset += size; } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs index 018c5fdc0..a80dcbc87 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs @@ -62,7 +62,6 @@ namespace Ryujinx.Graphics.Gpu.Shader var result = new TextureBindingInfo( target, - descriptor.Set, descriptor.Binding, descriptor.ArrayLength, descriptor.CbufSlot, @@ -86,12 +85,11 @@ namespace Ryujinx.Graphics.Gpu.Shader ImageBindings[i] = stage.Info.Images.Select(descriptor => { Target target = ShaderTexture.GetTarget(descriptor.Type); - FormatInfo formatInfo = ShaderTexture.GetFormatInfo(descriptor.Format); + Format format = ShaderTexture.GetFormat(descriptor.Format); var result = new TextureBindingInfo( target, - formatInfo, - descriptor.Set, + format, descriptor.Binding, descriptor.ArrayLength, descriptor.CbufSlot, diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs index 3837092c9..b08c44d67 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs @@ -125,18 +125,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache CompressionAlgorithm algorithm = CompressionAlgorithm.None; Read(ref algorithm); - switch (algorithm) + if (algorithm == CompressionAlgorithm.Deflate) { - case CompressionAlgorithm.None: - break; - case CompressionAlgorithm.Deflate: - _activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true); - break; - case CompressionAlgorithm.Brotli: - _activeStream = new BrotliStream(_stream, CompressionMode.Decompress, true); - break; - default: - throw new ArgumentException($"Invalid compression algorithm \"{algorithm}\""); + _activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true); } } @@ -148,18 +139,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { Write(ref algorithm); - switch (algorithm) + if (algorithm == CompressionAlgorithm.Deflate) { - case CompressionAlgorithm.None: - break; - case CompressionAlgorithm.Deflate: - _activeStream = new DeflateStream(_stream, CompressionLevel.Fastest, true); - break; - case CompressionAlgorithm.Brotli: - _activeStream = new BrotliStream(_stream, CompressionLevel.Fastest, true); - break; - default: - throw new ArgumentException($"Invalid compression algorithm \"{algorithm}\""); + _activeStream = new DeflateStream(_stream, CompressionLevel.SmallestSize, true); } } @@ -195,7 +177,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache switch (algorithm) { case CompressionAlgorithm.None: - stream.ReadExactly(data); + stream.Read(data); break; case CompressionAlgorithm.Deflate: stream = new DeflateStream(stream, CompressionMode.Decompress, true); @@ -205,14 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } stream.Dispose(); break; - case CompressionAlgorithm.Brotli: - stream = new BrotliStream(stream, CompressionMode.Decompress, true); - for (int offset = 0; offset < data.Length;) - { - offset += stream.Read(data[offset..]); - } - stream.Dispose(); - break; } } @@ -232,12 +206,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache stream.Write(data); break; case CompressionAlgorithm.Deflate: - stream = new DeflateStream(stream, CompressionLevel.Fastest, true); - stream.Write(data); - stream.Dispose(); - break; - case CompressionAlgorithm.Brotli: - stream = new BrotliStream(stream, CompressionLevel.Fastest, true); + stream = new DeflateStream(stream, CompressionLevel.SmallestSize, true); stream.Write(data); stream.Dispose(); break; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs index 86d3de07d..96ddbb513 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs @@ -14,10 +14,5 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// Deflate compression (RFC 1951). /// Deflate, - - /// - /// Brotli compression (RFC 7932). - /// - Brotli, } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs index cecfe9acf..c4ce0b870 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// Compression algorithm public static CompressionAlgorithm GetCompressionAlgorithm() { - return CompressionAlgorithm.Brotli; + return CompressionAlgorithm.Deflate; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs index 08cd3bb02..59d2cfb3f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin); - dataFileStream.ReadExactly(cb1Data); + dataFileStream.Read(cb1Data); BinarySerializer.ReadCompressed(dataFileStream, guestCode); _cache[index] = (guestCode, cb1Data); @@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin); byte[] cachedCode = new byte[entry.CodeSize]; byte[] cachedCb1Data = new byte[entry.Cb1DataSize]; - dataFileStream.ReadExactly(cachedCb1Data); + dataFileStream.Read(cachedCb1Data); BinarySerializer.ReadCompressed(dataFileStream, cachedCode); if (data.SequenceEqual(cachedCode) && cb1Data.SequenceEqual(cachedCb1Data)) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index c36fc0ada..ea54049c2 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -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 = 7353; + private const uint CodeGenVersion = 5936; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d89eebabf..0d562b0da 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Gpu.Shader _reservedImages = rrc.ReservedImages; } - public SetBindingPair CreateConstantBufferBinding(int index) + public int CreateConstantBufferBinding(int index) { int binding; @@ -64,10 +64,10 @@ namespace Ryujinx.Graphics.Gpu.Shader binding = _resourceCounts.UniformBuffersCount++; } - return new SetBindingPair(_context.Capabilities.UniformBufferSetIndex, binding + _reservedConstantBuffers); + return binding + _reservedConstantBuffers; } - public SetBindingPair CreateImageBinding(int count, bool isBuffer) + public int CreateImageBinding(int count, bool isBuffer) { int binding; @@ -96,10 +96,10 @@ namespace Ryujinx.Graphics.Gpu.Shader _resourceCounts.ImagesCount += count; } - return new SetBindingPair(_context.Capabilities.ImageSetIndex, binding + _reservedImages); + return binding + _reservedImages; } - public SetBindingPair CreateStorageBufferBinding(int index) + public int CreateStorageBufferBinding(int index) { int binding; @@ -112,10 +112,10 @@ namespace Ryujinx.Graphics.Gpu.Shader binding = _resourceCounts.StorageBuffersCount++; } - return new SetBindingPair(_context.Capabilities.StorageBufferSetIndex, binding + _reservedStorageBuffers); + return binding + _reservedStorageBuffers; } - public SetBindingPair CreateTextureBinding(int count, bool isBuffer) + public int CreateTextureBinding(int count, bool isBuffer) { int binding; @@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Shader _resourceCounts.TexturesCount += count; } - return new SetBindingPair(_context.Capabilities.TextureSetIndex, binding + _reservedTextures); + return binding + _reservedTextures; } private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName) @@ -183,16 +183,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return maxPerStage * Constants.ShaderStages; } - public int CreateExtraSet() - { - if (_resourceCounts.SetsCount >= _context.Capabilities.MaximumExtraSets) - { - return -1; - } - - return _context.Capabilities.ExtraSetBaseIndex + _resourceCounts.SetsCount++; - } - public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision; public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs index 59ab378cf..126e3249c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs @@ -24,10 +24,5 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Total of images used by the shaders. /// public int ImagesCount; - - /// - /// Total of extra sets used by the shaders. - /// - public int SetsCount; } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 49823562f..ed56db3b3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; -using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Shader @@ -10,6 +9,13 @@ namespace Ryujinx.Graphics.Gpu.Shader /// class ShaderInfoBuilder { + private const int TotalSets = 4; + + private const int UniformSetIndex = 0; + private const int StorageSetIndex = 1; + private const int TextureSetIndex = 2; + private const int ImageSetIndex = 3; + private const ResourceStages SupportBufferStages = ResourceStages.Compute | ResourceStages.Vertex | @@ -30,8 +36,8 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly int _reservedTextures; private readonly int _reservedImages; - private List[] _resourceDescriptors; - private List[] _resourceUsages; + private readonly List[] _resourceDescriptors; + private readonly List[] _resourceUsages; /// /// Creates a new shader info builder. @@ -45,27 +51,17 @@ namespace Ryujinx.Graphics.Gpu.Shader _fragmentOutputMap = -1; - int uniformSetIndex = context.Capabilities.UniformBufferSetIndex; - int storageSetIndex = context.Capabilities.StorageBufferSetIndex; - int textureSetIndex = context.Capabilities.TextureSetIndex; - int imageSetIndex = context.Capabilities.ImageSetIndex; + _resourceDescriptors = new List[TotalSets]; + _resourceUsages = new List[TotalSets]; - int totalSets = Math.Max(uniformSetIndex, storageSetIndex); - totalSets = Math.Max(totalSets, textureSetIndex); - totalSets = Math.Max(totalSets, imageSetIndex); - totalSets++; - - _resourceDescriptors = new List[totalSets]; - _resourceUsages = new List[totalSets]; - - for (int index = 0; index < totalSets; index++) + for (int index = 0; index < TotalSets; index++) { _resourceDescriptors[index] = new(); _resourceUsages[index] = new(); } - AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1); - AddUsage(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1); + AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + AddUsage(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); ResourceReservationCounts rrc = new(!context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute); @@ -77,25 +73,16 @@ namespace Ryujinx.Graphics.Gpu.Shader // TODO: Handle that better? Maybe we should only set the binding that are really needed on each shader. ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages; - PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); - PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true); - PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures); - PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true); + PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, UniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); + PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, StorageSetIndex, 0, rrc.ReservedStorageBuffers); + PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, TextureSetIndex, 0, rrc.ReservedTextures); + PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, ImageSetIndex, 0, rrc.ReservedImages); } - /// - /// Populates descriptors and usages for vertex as compute and transform feedback emulation reserved resources. - /// - /// Shader stages where the resources are used - /// Resource type - /// Resource set index where the resources are used - /// First binding number - /// Amount of bindings - /// True if the binding is written from the shader, false otherwise - private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) + private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count) { AddDescriptor(stages, type, setIndex, start, count); - AddUsage(stages, type, setIndex, start, count, write); + AddUsage(stages, type, setIndex, start, count); } /// @@ -140,23 +127,18 @@ namespace Ryujinx.Graphics.Gpu.Shader int textureBinding = _reservedTextures + stageIndex * texturesPerStage * 2; int imageBinding = _reservedImages + stageIndex * imagesPerStage * 2; - int uniformSetIndex = _context.Capabilities.UniformBufferSetIndex; - int storageSetIndex = _context.Capabilities.StorageBufferSetIndex; - int textureSetIndex = _context.Capabilities.TextureSetIndex; - int imageSetIndex = _context.Capabilities.ImageSetIndex; + AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage); + AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage); + AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage); + AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage); - AddDescriptor(stages, ResourceType.UniformBuffer, uniformSetIndex, uniformBinding, uniformsPerStage); - AddDescriptor(stages, ResourceType.StorageBuffer, storageSetIndex, storageBinding, storagesPerStage); - AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, textureSetIndex, textureBinding, texturesPerStage); - AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, imageSetIndex, imageBinding, imagesPerStage); + AddArrayDescriptors(info.Textures, stages, TextureSetIndex, isImage: false); + AddArrayDescriptors(info.Images, stages, TextureSetIndex, isImage: true); - AddArrayDescriptors(info.Textures, stages, isImage: false); - AddArrayDescriptors(info.Images, stages, isImage: true); - - AddUsage(info.CBuffers, stages, isStorage: false); - AddUsage(info.SBuffers, stages, isStorage: true); - AddUsage(info.Textures, stages, isImage: false); - AddUsage(info.Images, stages, isImage: true); + AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false); + AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true); + AddUsage(info.Textures, stages, TextureSetIndex, isImage: false); + AddUsage(info.Images, stages, ImageSetIndex, isImage: true); } /// @@ -195,8 +177,9 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Textures to be added /// Stages where the textures are used + /// Descriptor set index where the textures will be bound /// True for images, false for textures - private void AddArrayDescriptors(IEnumerable textures, ResourceStages stages, bool isImage) + private void AddArrayDescriptors(IEnumerable textures, ResourceStages stages, int setIndex, bool isImage) { foreach (TextureDescriptor texture in textures) { @@ -204,7 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ResourceType type = GetTextureResourceType(texture, isImage); - GetDescriptors(texture.Set).Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); } } } @@ -217,12 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Descriptor set number where the resource will be bound /// Binding number where the resource will be bound /// Number of resources bound at the binding location - /// True if the binding is written from the shader, false otherwise - private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false) + private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) { for (int index = 0; index < count; index++) { - _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages)); } } @@ -231,17 +213,17 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Buffers to be added /// Stages where the buffers are used + /// Descriptor set index where the buffers will be bound /// True for storage buffers, false for uniform buffers - private void AddUsage(IEnumerable buffers, ResourceStages stages, bool isStorage) + private void AddUsage(IEnumerable buffers, ResourceStages stages, int setIndex, bool isStorage) { foreach (BufferDescriptor buffer in buffers) { - GetUsages(buffer.Set).Add(new ResourceUsage( + _resourceUsages[setIndex].Add(new ResourceUsage( buffer.Binding, 1, isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, - stages, - buffer.Flags.HasFlag(BufferUsageFlags.Write))); + stages)); } } @@ -250,70 +232,18 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Textures to be added /// Stages where the textures are used + /// Descriptor set index where the textures will be bound /// True for images, false for textures - private void AddUsage(IEnumerable textures, ResourceStages stages, bool isImage) + private void AddUsage(IEnumerable textures, ResourceStages stages, int setIndex, bool isImage) { foreach (TextureDescriptor texture in textures) { ResourceType type = GetTextureResourceType(texture, isImage); - GetUsages(texture.Set).Add(new ResourceUsage( - texture.Binding, - texture.ArrayLength, - type, - stages, - texture.Flags.HasFlag(TextureUsageFlags.ImageStore))); + _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); } } - /// - /// Gets the list of resource descriptors for a given set index. A new list will be created if needed. - /// - /// Resource set index - /// List of resource descriptors - private List GetDescriptors(int setIndex) - { - if (_resourceDescriptors.Length <= setIndex) - { - int oldLength = _resourceDescriptors.Length; - Array.Resize(ref _resourceDescriptors, setIndex + 1); - - for (int index = oldLength; index <= setIndex; index++) - { - _resourceDescriptors[index] = new(); - } - } - - return _resourceDescriptors[setIndex]; - } - - /// - /// Gets the list of resource usages for a given set index. A new list will be created if needed. - /// - /// Resource set index - /// List of resource usages - private List GetUsages(int setIndex) - { - if (_resourceUsages.Length <= setIndex) - { - int oldLength = _resourceUsages.Length; - Array.Resize(ref _resourceUsages, setIndex + 1); - - for (int index = oldLength; index <= setIndex; index++) - { - _resourceUsages[index] = new(); - } - } - - return _resourceUsages[setIndex]; - } - - /// - /// Gets a resource type from a texture descriptor. - /// - /// Texture descriptor - /// Whether the texture is a image texture (writable) or not (sampled) - /// Resource type private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage) { bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; @@ -348,12 +278,10 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader information public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false) { - int totalSets = _resourceDescriptors.Length; + var descriptors = new ResourceDescriptorCollection[TotalSets]; + var usages = new ResourceUsageCollection[TotalSets]; - var descriptors = new ResourceDescriptorCollection[totalSets]; - var usages = new ResourceUsageCollection[totalSets]; - - for (int index = 0; index < totalSets; index++) + for (int index = 0; index < TotalSets; index++) { descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly()); usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 59cd4c8a6..3b2368537 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu bool isLinear, int gobBlocksInY, Format format, - byte bytesPerPixel, + int bytesPerPixel, ImageCrop crop, Action acquireCallback, Action releaseCallback, diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/AreaScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/AreaScalingFilter.cs deleted file mode 100644 index 9b19f2f26..000000000 --- a/src/Ryujinx.Graphics.OpenGL/Effects/AreaScalingFilter.cs +++ /dev/null @@ -1,106 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.OpenGL.Image; -using System; -using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper; - -namespace Ryujinx.Graphics.OpenGL.Effects -{ - internal class AreaScalingFilter : IScalingFilter - { - private readonly OpenGLRenderer _renderer; - private int _inputUniform; - private int _outputUniform; - private int _srcX0Uniform; - private int _srcX1Uniform; - private int _srcY0Uniform; - private int _scalingShaderProgram; - private int _srcY1Uniform; - private int _dstX0Uniform; - private int _dstX1Uniform; - private int _dstY0Uniform; - private int _dstY1Uniform; - - public float Level { get; set; } - - public AreaScalingFilter(OpenGLRenderer renderer) - { - Initialize(); - - _renderer = renderer; - } - - public void Dispose() - { - if (_scalingShaderProgram != 0) - { - GL.DeleteProgram(_scalingShaderProgram); - } - } - - private void Initialize() - { - var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl"); - - _scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader); - - _inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source"); - _outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput"); - - _srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0"); - _srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1"); - _srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0"); - _srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1"); - _dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0"); - _dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1"); - _dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0"); - _dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1"); - } - - public void Run( - TextureView view, - TextureView destinationTexture, - int width, - int height, - Extents2D source, - Extents2D destination) - { - int previousProgram = GL.GetInteger(GetPName.CurrentProgram); - int previousUnit = GL.GetInteger(GetPName.ActiveTexture); - GL.ActiveTexture(TextureUnit.Texture0); - int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D); - - GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); - - int threadGroupWorkRegionDim = 16; - int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - // Scaling pass - GL.UseProgram(_scalingShaderProgram); - view.Bind(0); - GL.Uniform1(_inputUniform, 0); - GL.Uniform1(_outputUniform, 0); - GL.Uniform1(_srcX0Uniform, (float)source.X1); - GL.Uniform1(_srcX1Uniform, (float)source.X2); - GL.Uniform1(_srcY0Uniform, (float)source.Y1); - GL.Uniform1(_srcY1Uniform, (float)source.Y2); - GL.Uniform1(_dstX0Uniform, (float)destination.X1); - GL.Uniform1(_dstX1Uniform, (float)destination.X2); - GL.Uniform1(_dstY0Uniform, (float)destination.Y1); - GL.Uniform1(_dstY1Uniform, (float)destination.Y2); - GL.DispatchCompute(dispatchX, dispatchY, 1); - - GL.UseProgram(previousProgram); - GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); - - (_renderer.Pipeline as Pipeline).RestoreImages1And2(); - - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding); - - GL.ActiveTexture((TextureUnit)previousUnit); - } - } -} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs index 0522e28e0..1a130bebb 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects private int _srcY0Uniform; private int _scalingShaderProgram; private int _sharpeningShaderProgram; - private float _sharpeningLevel = 1; + private float _scale = 1; private int _srcY1Uniform; private int _dstX0Uniform; private int _dstX1Uniform; @@ -30,10 +30,10 @@ namespace Ryujinx.Graphics.OpenGL.Effects public float Level { - get => _sharpeningLevel; + get => _scale; set { - _sharpeningLevel = MathF.Max(0.01f, value); + _scale = MathF.Max(0.01f, value); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs b/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs index 637b2fba8..c25fe5b25 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs @@ -1,5 +1,4 @@ using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Logging; namespace Ryujinx.Graphics.OpenGL.Effects { @@ -7,7 +6,18 @@ namespace Ryujinx.Graphics.OpenGL.Effects { public static int CompileProgram(string shaderCode, ShaderType shaderType) { - return CompileProgram(new string[] { shaderCode }, shaderType); + var shader = GL.CreateShader(shaderType); + GL.ShaderSource(shader, shaderCode); + GL.CompileShader(shader); + + var program = GL.CreateProgram(); + GL.AttachShader(program, shader); + GL.LinkProgram(program); + + GL.DetachShader(program, shader); + GL.DeleteShader(shader); + + return program; } public static int CompileProgram(string[] shaders, ShaderType shaderType) @@ -16,15 +26,6 @@ namespace Ryujinx.Graphics.OpenGL.Effects GL.ShaderSource(shader, shaders.Length, shaders, (int[])null); GL.CompileShader(shader); - GL.GetShader(shader, ShaderParameter.CompileStatus, out int isCompiled); - if (isCompiled == 0) - { - string log = GL.GetShaderInfoLog(shader); - Logger.Error?.Print(LogClass.Gpu, $"Failed to compile effect shader:\n\n{log}\n"); - GL.DeleteShader(shader); - return 0; - } - var program = GL.CreateProgram(); GL.AttachShader(program, shader); GL.LinkProgram(program); diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl deleted file mode 100644 index 0fe20d3f9..000000000 --- a/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl +++ /dev/null @@ -1,119 +0,0 @@ -#version 430 core -precision mediump float; -layout (local_size_x = 16, local_size_y = 16) in; -layout(rgba8, binding = 0, location=0) uniform image2D imgOutput; -layout( location=1 ) uniform sampler2D Source; -layout( location=2 ) uniform float srcX0; -layout( location=3 ) uniform float srcX1; -layout( location=4 ) uniform float srcY0; -layout( location=5 ) uniform float srcY1; -layout( location=6 ) uniform float dstX0; -layout( location=7 ) uniform float dstX1; -layout( location=8 ) uniform float dstY0; -layout( location=9 ) uniform float dstY1; - -/***** Area Sampling *****/ - -// By Sam Belliveau and Filippo Tarpini. Public Domain license. -// Effectively a more accurate sharp bilinear filter when upscaling, -// that also works as a mathematically perfect downscale filter. -// https://entropymine.com/imageworsener/pixelmixing/ -// https://github.com/obsproject/obs-studio/pull/1715 -// https://legacy.imagemagick.org/Usage/filter/ -vec4 AreaSampling(vec2 xy) -{ - // Determine the sizes of the source and target images. - vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0)); - vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0)); - vec2 inverted_target_size = vec2(1.0) / target_size; - - // Compute the top-left and bottom-right corners of the target pixel box. - vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1)); - vec2 t_end = t_beg + vec2(1.0, 1.0); - - // Convert the target pixel box to source pixel box. - vec2 beg = t_beg * inverted_target_size * source_size; - vec2 end = t_end * inverted_target_size * source_size; - - // Compute the top-left and bottom-right corners of the pixel box. - ivec2 f_beg = ivec2(beg); - ivec2 f_end = ivec2(end); - - // Compute how much of the start and end pixels are covered horizontally & vertically. - float area_w = 1.0 - fract(beg.x); - float area_n = 1.0 - fract(beg.y); - float area_e = fract(end.x); - float area_s = fract(end.y); - - // Compute the areas of the corner pixels in the pixel box. - float area_nw = area_n * area_w; - float area_ne = area_n * area_e; - float area_sw = area_s * area_w; - float area_se = area_s * area_e; - - // Initialize the color accumulator. - vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0); - - // Accumulate corner pixels. - avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0); - avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0); - avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0); - avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0); - - // Determine the size of the pixel box. - int x_range = int(f_end.x - f_beg.x - 0.5); - int y_range = int(f_end.y - f_beg.y - 0.5); - - // Accumulate top and bottom edge pixels. - for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) - { - avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0); - avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0); - } - - // Accumulate left and right edge pixels and all the pixels in between. - for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) - { - avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0); - avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0); - - for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) - { - avg_color += texelFetch(Source, ivec2(x, y), 0); - } - } - - // Compute the area of the pixel box that was sampled. - float area_corners = area_nw + area_ne + area_sw + area_se; - float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e); - float area_center = float(x_range) * float(y_range); - - // Return the normalized average color. - return avg_color / (area_corners + area_edges + area_center); -} - -float insideBox(vec2 v, vec2 bLeft, vec2 tRight) { - vec2 s = step(bLeft, v) - step(tRight, v); - return s.x * s.y; -} - -vec2 translateDest(vec2 pos) { - vec2 translatedPos = vec2(pos.x, pos.y); - translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x; - translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1 : translatedPos.y; - return translatedPos; -} - -void main() -{ - vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1); - vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0); - ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - if (insideBox(loc, bLeft, tRight) == 0) { - imageStore(imgOutput, loc, vec4(0, 0, 0, 1)); - return; - } - - vec4 outColor = AreaSampling(loc); - imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1)); -} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl index 3c7d485b1..8e8755db2 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl +++ b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl @@ -85,4 +85,4 @@ void main() { CurrFilter(gxy); gxy.x -= 8u; CurrFilter(gxy); -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs index 490c0c585..434f25900 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -9,11 +10,11 @@ namespace Ryujinx.Graphics.OpenGL.Image { static class FormatConverter { - public unsafe static MemoryOwner ConvertS8D24ToD24S8(ReadOnlySpan data) + public unsafe static IMemoryOwner ConvertS8D24ToD24S8(ReadOnlySpan data) { - MemoryOwner outputMemory = MemoryOwner.Rent(data.Length); + IMemoryOwner outputMemory = ByteMemoryPool.Rent(data.Length); - Span output = outputMemory.Span; + Span output = outputMemory.Memory.Span; int start = 0; diff --git a/src/Ryujinx.Graphics.OpenGL/Image/ImageArray.cs b/src/Ryujinx.Graphics.OpenGL/Image/ImageArray.cs index 3486f29df..1c5acedf3 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/ImageArray.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/ImageArray.cs @@ -1,5 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.GAL; +using System; namespace Ryujinx.Graphics.OpenGL.Image { @@ -18,6 +19,14 @@ namespace Ryujinx.Graphics.OpenGL.Image _images = new TextureRef[size]; } + public void SetFormats(int index, GAL.Format[] imageFormats) + { + for (int i = 0; i < imageFormats.Length; i++) + { + _images[index + i].Format = imageFormats[i]; + } + } + public void SetImages(int index, ITexture[] images) { for (int i = 0; i < images.Length; i++) @@ -27,7 +36,6 @@ namespace Ryujinx.Graphics.OpenGL.Image if (image is TextureBase imageBase) { _images[index + i].Handle = imageBase.Handle; - _images[index + i].Format = imageBase.Format; } else { @@ -55,9 +63,5 @@ namespace Ryujinx.Graphics.OpenGL.Image } } } - - public void Dispose() - { - } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureArray.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureArray.cs index 41ac058c1..d70b0a008 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureArray.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureArray.cs @@ -48,9 +48,5 @@ namespace Ryujinx.Graphics.OpenGL.Image } } } - - public void Dispose() - { - } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 22f4c04cd..a8196541a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -1,7 +1,7 @@ using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; +using System.Buffers; namespace Ryujinx.Graphics.OpenGL.Image { @@ -55,9 +55,9 @@ namespace Ryujinx.Graphics.OpenGL.Image } /// - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { - var dataSpan = data.Span; + var dataSpan = data.Memory.Span; Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]); @@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } /// - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { throw new NotSupportedException(); } /// - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs index 0ebafb04e..79c6cb685 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image internalFormat = (SizedInternalFormat)format.PixelInternalFormat; } - int levels = Info.Levels; + int levels = Info.GetLevelsClamped(); switch (Info.Target) { diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index b0859c49e..8a18e6132 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -1,8 +1,8 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; +using System.Buffers; using System.Diagnostics; namespace Ryujinx.Graphics.OpenGL.Image @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.OpenGL.Image pixelInternalFormat = format.PixelInternalFormat; } - int levels = Info.Levels; + int levels = Info.GetLevelsClamped(); GL.TextureView( Handle, @@ -267,7 +267,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public unsafe PinnedSpan GetData() { int size = 0; - int levels = Info.Levels; + int levels = Info.GetLevelsClamped(); for (int level = 0; level < levels; level++) { @@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.OpenGL.Image faces = 6; } - int levels = Info.Levels; + int levels = Info.GetLevelsClamped(); for (int level = 0; level < levels; level++) { @@ -448,13 +448,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { using (data = EnsureDataFormat(data)) { unsafe { - var dataSpan = data.Span; + var dataSpan = data.Memory.Span; fixed (byte* ptr = dataSpan) { ReadFrom((IntPtr)ptr, dataSpan.Length); @@ -463,13 +463,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { using (data = EnsureDataFormat(data)) { unsafe { - fixed (byte* ptr = data.Span) + fixed (byte* ptr = data.Memory.Span) { int width = Math.Max(Info.Width >> level, 1); int height = Math.Max(Info.Height >> level, 1); @@ -480,7 +480,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { using (data = EnsureDataFormat(data)) { @@ -489,7 +489,7 @@ namespace Ryujinx.Graphics.OpenGL.Image unsafe { - fixed (byte* ptr = data.Span) + fixed (byte* ptr = data.Memory.Span) { ReadFrom2D( (IntPtr)ptr, @@ -522,13 +522,13 @@ namespace Ryujinx.Graphics.OpenGL.Image ReadFrom2D(data, layer, level, x, y, width, height, mipSize); } - private MemoryOwner EnsureDataFormat(MemoryOwner data) + private IMemoryOwner EnsureDataFormat(IMemoryOwner data) { if (Format == Format.S8UintD24Unorm) { using (data) { - return FormatConverter.ConvertS8D24ToD24S8(data.Span); + return FormatConverter.ConvertS8D24ToD24S8(data.Memory.Span); } } @@ -716,7 +716,7 @@ namespace Ryujinx.Graphics.OpenGL.Image int width = Info.Width; int height = Info.Height; int depth = Info.Depth; - int levels = Info.Levels; + int levels = Info.GetLevelsClamped(); int offset = 0; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 9fcdf1ad7..2a39ae446 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -61,9 +61,7 @@ namespace Ryujinx.Graphics.OpenGL { BufferCount++; - var memType = access & GAL.BufferAccess.MemoryTypeMask; - - if (memType == GAL.BufferAccess.HostMemory) + if (access.HasFlag(GAL.BufferAccess.FlushPersistent)) { BufferHandle handle = Buffer.CreatePersistent(size); @@ -77,6 +75,11 @@ namespace Ryujinx.Graphics.OpenGL } } + public BufferHandle CreateBuffer(int size, GAL.BufferAccess access, BufferHandle storageHint) + { + return CreateBuffer(size, access); + } + public BufferHandle CreateBuffer(nint pointer, int size) { throw new NotSupportedException(); @@ -145,7 +148,6 @@ namespace Ryujinx.Graphics.OpenGL return new Capabilities( api: TargetApi.OpenGL, vendorName: GpuVendor, - memoryType: SystemMemoryType.BackendManaged, hasFrontFacingBug: intelWindows, hasVectorIndexingBug: amdWindows, needsFragmentOutputSpecialization: false, @@ -187,12 +189,6 @@ namespace Ryujinx.Graphics.OpenGL supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, supportsDepthClipControl: true, - uniformBufferSetIndex: 0, - storageBufferSetIndex: 1, - textureSetIndex: 2, - imageSetIndex: 3, - extraSetBaseIndex: 0, - maximumExtraSets: 0, maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? maximumStorageBuffersPerStage: 16, maximumTexturesPerStage: 32, @@ -202,8 +198,7 @@ namespace Ryujinx.Graphics.OpenGL shaderSubgroupSize: Constants.MaxSubgroupSize, storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment, textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment, - gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan. - maximumGpuMemory: 0); + gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan. } public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 27aacac15..6d066bb67 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.OpenGL private readonly Vector4[] _fpIsBgra = new Vector4[SupportBuffer.FragmentIsBgraCount]; - private readonly TextureBase[] _images; + private readonly (TextureBase, Format)[] _images; private TextureBase _unit0Texture; private Sampler _unit0Sampler; @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL _fragmentOutputMap = uint.MaxValue; _componentMasks = uint.MaxValue; - _images = new TextureBase[SavedImages]; + _images = new (TextureBase, Format)[SavedImages]; _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers]; _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers]; @@ -935,11 +935,11 @@ namespace Ryujinx.Graphics.OpenGL SetFrontFace(_frontFace = frontFace.Convert()); } - public void SetImage(ShaderStage stage, int binding, ITexture texture) + public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { if ((uint)binding < SavedImages) { - _images[binding] = texture as TextureBase; + _images[binding] = (texture as TextureBase, imageFormat); } if (texture == null) @@ -950,7 +950,7 @@ namespace Ryujinx.Graphics.OpenGL TextureBase texBase = (TextureBase)texture; - SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format); + SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat); if (format != 0) { @@ -963,11 +963,6 @@ namespace Ryujinx.Graphics.OpenGL (array as ImageArray).Bind(binding); } - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) - { - throw new NotSupportedException("OpenGL does not support descriptor sets."); - } - public void SetIndexBuffer(BufferRange buffer, IndexType type) { _elementsType = type.Convert(); @@ -1317,11 +1312,6 @@ namespace Ryujinx.Graphics.OpenGL (array as TextureArray).Bind(binding); } - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) - { - throw new NotSupportedException("OpenGL does not support descriptor sets."); - } - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { if (_tfEnabled) @@ -1622,11 +1612,11 @@ namespace Ryujinx.Graphics.OpenGL { for (int i = 0; i < SavedImages; i++) { - TextureBase texBase = _images[i]; + (TextureBase texBase, Format imageFormat) = _images[i]; if (texBase != null) { - SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format); + SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat); if (format != 0) { diff --git a/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj index f3071f486..3d64da99b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj +++ b/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -21,7 +21,6 @@ - diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index 285ab725e..6bcfefa4e 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -373,16 +373,6 @@ namespace Ryujinx.Graphics.OpenGL _isLinear = false; _scalingFilter.Level = _scalingFilterLevel; - RecreateUpscalingTexture(); - break; - case ScalingFilter.Area: - if (_scalingFilter is not AreaScalingFilter) - { - _scalingFilter?.Dispose(); - _scalingFilter = new AreaScalingFilter(_renderer); - } - _isLinear = false; - RecreateUpscalingTexture(); break; } diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index 11d4e3c11..ead1c5e67 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -4,16 +4,14 @@ namespace Ryujinx.Graphics.Shader { // New fields should be added to the end of the struct to keep disk shader cache compatibility. - public readonly int Set; public readonly int Binding; public readonly byte Slot; public readonly byte SbCbSlot; public readonly ushort SbCbOffset; public readonly BufferUsageFlags Flags; - public BufferDescriptor(int set, int binding, int slot) + public BufferDescriptor(int binding, int slot) { - Set = set; Binding = binding; Slot = (byte)slot; SbCbSlot = 0; @@ -21,9 +19,8 @@ namespace Ryujinx.Graphics.Shader Flags = BufferUsageFlags.None; } - public BufferDescriptor(int set, int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags) + public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags) { - Set = set; Binding = binding; Slot = (byte)slot; SbCbSlot = (byte)sbCbSlot; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 4308b08f8..f0e57b534 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); + context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; string texCall; @@ -639,7 +639,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) { - TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; + TextureDefinition textureDefinition = context.Properties.Textures[texOp.Binding]; string name = textureDefinition.Name; if (textureDefinition.ArrayLength != 1) @@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (texOp.IsSeparate) { - TextureDefinition samplerDefinition = context.Properties.Textures[texOp.GetSamplerSetAndBinding()]; + TextureDefinition samplerDefinition = context.Properties.Textures[texOp.SamplerBinding]; string samplerName = samplerDefinition.Name; if (samplerDefinition.ArrayLength != 1) @@ -665,7 +665,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) { - TextureDefinition definition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; + TextureDefinition definition = context.Properties.Images[texOp.Binding]; string name = definition.Name; if (definition.ArrayLength != 1) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index cc7977f84..2b1fdf44c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -33,9 +33,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary LocalMemories { get; } = new(); public Dictionary SharedMemories { get; } = new(); - public Dictionary SamplersTypes { get; } = new(); - public Dictionary Samplers { get; } = new(); - public Dictionary Images { get; } = new(); + public Dictionary SamplersTypes { get; } = new(); + public Dictionary Samplers { get; } = new(); + public Dictionary Images { get; } = new(); public Dictionary Inputs { get; } = new(); public Dictionary Outputs { get; } = new(); @@ -98,6 +98,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Logger = parameters.Logger; TargetApi = parameters.TargetApi; + AddCapability(Capability.Shader); + AddCapability(Capability.Float64); + + SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450); + Delegates = new SpirvDelegates(this); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 55d35bf0d..37df4df80 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -208,13 +208,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant); - context.Samplers.Add(new(sampler.Set, sampler.Binding), new SamplerDeclaration( + context.Samplers.Add(sampler.Binding, new SamplerDeclaration( imageType, sampledImageType, sampledImagePointerType, sampledImageVariable, sampler.ArrayLength != 1)); - context.SamplersTypes.Add(new(sampler.Set, sampler.Binding), sampler.Type); + context.SamplersTypes.Add(sampler.Binding, sampler.Type); context.Name(sampledImageVariable, sampler.Name); context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); @@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var imageVariable = context.Variable(imageArrayPointerType, StorageClass.UniformConstant); - context.Images.Add(new(image.Set, image.Binding), new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1)); + context.Images.Add(image.Binding, new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1)); context.Name(imageVariable, image.Name); context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 6206985d8..34f8532a6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -602,7 +602,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()]; + ImageDeclaration declaration = context.Images[texOp.Binding]; SpvInstruction image = declaration.Image; SpvInstruction resultType = context.GetType(componentType); @@ -681,7 +681,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()]; + ImageDeclaration declaration = context.Images[texOp.Binding]; SpvInstruction image = declaration.Image; if (declaration.IsIndexed) @@ -738,7 +738,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()]; + ImageDeclaration declaration = context.Images[texOp.Binding]; SpvInstruction image = declaration.Image; if (declaration.IsIndexed) @@ -837,7 +837,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; + SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); int pCount = texOp.Type.GetDimensions(); @@ -1161,7 +1161,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; + SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); int coordsCount = texOp.Type.GetDimensions(); @@ -1433,7 +1433,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv int srcIndex = 0; - SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; + SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); image = context.Image(declaration.ImageType, image); @@ -1449,7 +1449,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv int srcIndex = 0; - SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; + SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); image = context.Image(declaration.ImageType, image); @@ -1460,7 +1460,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else { - var type = context.SamplersTypes[texOp.GetTextureSetAndBinding()]; + var type = context.SamplersTypes[texOp.Binding]; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); @@ -1889,7 +1889,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { image = context.Load(declaration.ImageType, image); - SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()]; + SamplerDeclaration samplerDeclaration = context.Samplers[texOp.SamplerBinding]; SpvInstruction sampler = samplerDeclaration.Image; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index b259dde28..ccfdc46d0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -43,10 +43,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv CodeGenContext context = new(info, parameters, instPool, integerPool); - context.AddCapability(Capability.Shader); - - context.SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450); - context.AddCapability(Capability.GroupNonUniformBallot); context.AddCapability(Capability.GroupNonUniformShuffle); context.AddCapability(Capability.GroupNonUniformVote); @@ -55,11 +51,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.ImageQuery); context.AddCapability(Capability.SampledBuffer); - if (parameters.HostCapabilities.SupportsShaderFloat64) - { - context.AddCapability(Capability.Float64); - } - if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline) { context.AddCapability(Capability.TransformFeedback); @@ -67,8 +58,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (parameters.Definitions.Stage == ShaderStage.Fragment) { - if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)) || - context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.PrimitiveId))) + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer))) { context.AddCapability(Capability.Geometry); } diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 4e6d6edf9..3dc4ad907 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -27,43 +27,34 @@ namespace Ryujinx.Graphics.Shader ReadOnlySpan GetCode(ulong address, int minimumSize); /// - /// Gets the binding number of a constant buffer. + /// Queries the binding number of a constant buffer. /// /// Constant buffer index /// Binding number - SetBindingPair CreateConstantBufferBinding(int index); + int CreateConstantBufferBinding(int index); /// - /// Gets the binding number of an image. + /// Queries the binding number of an image. /// /// For array of images, the number of elements of the array, otherwise it should be 1 /// Indicates if the image is a buffer image /// Binding number - SetBindingPair CreateImageBinding(int count, bool isBuffer); + int CreateImageBinding(int count, bool isBuffer); /// - /// Gets the binding number of a storage buffer. + /// Queries the binding number of a storage buffer. /// /// Storage buffer index /// Binding number - SetBindingPair CreateStorageBufferBinding(int index); + int CreateStorageBufferBinding(int index); /// - /// Gets the binding number of a texture. + /// Queries the binding number of a texture. /// /// For array of textures, the number of elements of the array, otherwise it should be 1 /// Indicates if the texture is a buffer texture /// Binding number - SetBindingPair CreateTextureBinding(int count, bool isBuffer); - - /// - /// Gets the set index for an additional set, or -1 if there's no extra set available. - /// - /// Extra set index, or -1 if not available - int CreateExtraSet() - { - return -1; - } + int CreateTextureBinding(int count, bool isBuffer); /// /// Queries Local Size X for compute shaders. diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 3fcb821d3..40129252a 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -222,14 +222,30 @@ namespace Ryujinx.Graphics.Shader.Instructions context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; - case AtomOp.Min: - if (type == AtomSize.S32) + case AtomOp.And: + if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicMinS32(storageKind, e0, e1, value); + res = context.AtomicAnd(storageKind, e0, e1, value); } - else if (type == AtomSize.U32) + else { - res = context.AtomicMinU32(storageKind, e0, e1, value); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); + } + break; + case AtomOp.Xor: + if (type == AtomSize.S32 || type == AtomSize.U32) + { + res = context.AtomicXor(storageKind, e0, e1, value); + } + else + { + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); + } + break; + case AtomOp.Or: + if (type == AtomSize.S32 || type == AtomSize.U32) + { + res = context.AtomicOr(storageKind, e0, e1, value); } else { @@ -250,49 +266,20 @@ namespace Ryujinx.Graphics.Shader.Instructions context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; - case AtomOp.And: - if (type == AtomSize.S32 || type == AtomSize.U32) + case AtomOp.Min: + if (type == AtomSize.S32) { - res = context.AtomicAnd(storageKind, e0, e1, value); + res = context.AtomicMinS32(storageKind, e0, e1, value); + } + else if (type == AtomSize.U32) + { + res = context.AtomicMinU32(storageKind, e0, e1, value); } else { context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; - case AtomOp.Or: - if (type == AtomSize.S32 || type == AtomSize.U32) - { - res = context.AtomicOr(storageKind, e0, e1, value); - } - else - { - context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); - } - break; - case AtomOp.Xor: - if (type == AtomSize.S32 || type == AtomSize.U32) - { - res = context.AtomicXor(storageKind, e0, e1, value); - } - else - { - context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); - } - break; - case AtomOp.Exch: - if (type == AtomSize.S32 || type == AtomSize.U32) - { - res = context.AtomicSwap(storageKind, e0, e1, value); - } - else - { - context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); - } - break; - default: - context.TranslatorContext.GpuAccessor.Log($"Invalid atomic operation: {op}."); - break; } return res; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs index 1d8651254..630162ade 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.BVal) { - context.Copy(dest, context.ConditionalSelect(res, ConstF(1), ConstF(0))); + context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0))); } else { diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 383e82c69..0aac0ffa8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, @@ -286,7 +286,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, imm); - Operand res = context.ImageAtomic(type, format, flags, setAndBinding, sources); + Operand res = context.ImageAtomic(type, format, flags, binding, sources); context.Copy(d, res); } @@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFormat format = isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, handle); - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, format, @@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, handle); - context.ImageLoad(type, format, flags, setAndBinding, (int)componentMask, dests, sources); + context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources); } else { @@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFormat format = GetTextureFormat(size); - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, format, @@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, handle); - context.ImageLoad(type, format, flags, setAndBinding, compMask, dests, sources); + context.ImageLoad(type, format, flags, binding, compMask, dests, sources); switch (size) { @@ -552,7 +552,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, @@ -560,7 +560,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, imm); - context.ImageAtomic(type, format, flags, setAndBinding, sources); + context.ImageAtomic(type, format, flags, binding, sources); } private static void EmitSust( @@ -679,7 +679,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Coherent; } - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageStore, type, format, @@ -687,7 +687,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, handle); - context.ImageStore(type, format, flags, setAndBinding, sources); + context.ImageStore(type, format, flags, binding, sources); } private static int GetComponentSizeInBytesLog2(SuatomSize size) diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 2076262da..06daa26a0 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -885,7 +885,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return Register(dest++, RegisterType.Gpr); } - SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.Lod, type, TextureFormat.Unknown, @@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Shader.Instructions else { // The instruction component order is the inverse of GLSL's. - Operand res = context.Lod(type, flags, setAndBinding, compIndex ^ 1, sources); + Operand res = context.Lod(type, flags, binding, compIndex ^ 1, sources); res = context.FPMultiply(res, ConstF(256.0f)); @@ -1116,12 +1116,12 @@ namespace Ryujinx.Graphics.Shader.Instructions } TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; - SetBindingPair setAndBinding; + int binding; switch (query) { case TexQuery.TexHeaderDimension: - setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.TextureQuerySize, type, TextureFormat.Unknown, @@ -1140,13 +1140,13 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } - context.Copy(d, context.TextureQuerySize(type, flags, setAndBinding, compIndex, sources)); + context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources)); } } break; case TexQuery.TexHeaderTextureType: - setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding( + binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.TextureQuerySamples, type, TextureFormat.Unknown, @@ -1171,7 +1171,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (d != null) { - context.Copy(d, context.TextureQuerySamples(type, flags, setAndBinding, sources)); + context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources)); } } break; @@ -1191,7 +1191,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand[] dests, Operand[] sources) { - SetBindingPair setAndBinding = flags.HasFlag(TextureFlags.Bindless) ? default : context.ResourceManager.GetTextureOrImageBinding( + int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.TextureSample, type, TextureFormat.Unknown, @@ -1199,7 +1199,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureOperation.DefaultCbufSlot, handle); - context.TextureSample(type, flags, setAndBinding, componentMask, dests, sources); + context.TextureSample(type, flags, binding, componentMask, dests, sources); } private static SamplerType ConvertSamplerType(TexDim dimensions) diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 273a38a5b..8703e660e 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -156,26 +156,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return false; } - public static bool IsComparison(this Instruction inst) - { - switch (inst & Instruction.Mask) - { - case Instruction.CompareEqual: - case Instruction.CompareGreater: - case Instruction.CompareGreaterOrEqual: - case Instruction.CompareGreaterOrEqualU32: - case Instruction.CompareGreaterU32: - case Instruction.CompareLess: - case Instruction.CompareLessOrEqual: - case Instruction.CompareLessOrEqualU32: - case Instruction.CompareLessU32: - case Instruction.CompareNotEqual: - return true; - } - - return false; - } - public static bool IsTextureQuery(this Instruction inst) { inst &= Instruction.Mask; diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 7eee8f2e9..74ec5ca61 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -8,9 +8,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public TextureFormat Format { get; set; } public TextureFlags Flags { get; private set; } - public int Set { get; private set; } public int Binding { get; private set; } - public int SamplerSet { get; private set; } public int SamplerBinding { get; private set; } public TextureOperation( @@ -18,7 +16,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation SamplerType type, TextureFormat format, TextureFlags flags, - int set, int binding, int compIndex, Operand[] dests, @@ -27,28 +24,24 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Type = type; Format = format; Flags = flags; - Set = set; Binding = binding; - SamplerSet = -1; SamplerBinding = -1; } - public void TurnIntoArray(SetBindingPair setAndBinding) + public void TurnIntoArray(int binding) { Flags &= ~TextureFlags.Bindless; - Set = setAndBinding.SetIndex; - Binding = setAndBinding.Binding; + Binding = binding; } - public void TurnIntoArray(SetBindingPair textureSetAndBinding, SetBindingPair samplerSetAndBinding) + public void TurnIntoArray(int textureBinding, int samplerBinding) { - TurnIntoArray(textureSetAndBinding); + TurnIntoArray(textureBinding); - SamplerSet = samplerSetAndBinding.SetIndex; - SamplerBinding = samplerSetAndBinding.Binding; + SamplerBinding = samplerBinding; } - public void SetBinding(SetBindingPair setAndBinding) + public void SetBinding(int binding) { if ((Flags & TextureFlags.Bindless) != 0) { @@ -57,8 +50,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation RemoveSource(0); } - Set = setAndBinding.SetIndex; - Binding = setAndBinding.Binding; + Binding = binding; } public void SetLodLevelFlag() diff --git a/src/Ryujinx.Graphics.Shader/SetBindingPair.cs b/src/Ryujinx.Graphics.Shader/SetBindingPair.cs deleted file mode 100644 index 1e8a4f9c6..000000000 --- a/src/Ryujinx.Graphics.Shader/SetBindingPair.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Shader -{ - public readonly record struct SetBindingPair(int SetIndex, int Binding); -} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 867cae853..4068c4127 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -8,9 +8,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public TextureFormat Format { get; } public TextureFlags Flags { get; } - public int Set { get; } public int Binding { get; } - public int SamplerSet { get; } public int SamplerBinding { get; } public bool IsSeparate => SamplerBinding >= 0; @@ -20,9 +18,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr SamplerType type, TextureFormat format, TextureFlags flags, - int set, int binding, - int samplerSet, int samplerBinding, int index, params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) @@ -30,20 +26,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Type = type; Format = format; Flags = flags; - Set = set; Binding = binding; - SamplerSet = samplerSet; SamplerBinding = samplerBinding; } - - public SetBindingPair GetTextureSetAndBinding() - { - return new SetBindingPair(Set, Binding); - } - - public SetBindingPair GetSamplerSetAndBinding() - { - return new SetBindingPair(SamplerSet, SamplerBinding); - } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 53ed6bfcc..8c12c2aaf 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -6,15 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { private readonly Dictionary _constantBuffers; private readonly Dictionary _storageBuffers; - private readonly Dictionary _textures; - private readonly Dictionary _images; + private readonly Dictionary _textures; + private readonly Dictionary _images; private readonly Dictionary _localMemories; private readonly Dictionary _sharedMemories; public IReadOnlyDictionary ConstantBuffers => _constantBuffers; public IReadOnlyDictionary StorageBuffers => _storageBuffers; - public IReadOnlyDictionary Textures => _textures; - public IReadOnlyDictionary Images => _images; + public IReadOnlyDictionary Textures => _textures; + public IReadOnlyDictionary Images => _images; public IReadOnlyDictionary LocalMemories => _localMemories; public IReadOnlyDictionary SharedMemories => _sharedMemories; @@ -22,8 +22,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _constantBuffers = new Dictionary(); _storageBuffers = new Dictionary(); - _textures = new Dictionary(); - _images = new Dictionary(); + _textures = new Dictionary(); + _images = new Dictionary(); _localMemories = new Dictionary(); _sharedMemories = new Dictionary(); } @@ -40,12 +40,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public void AddOrUpdateTexture(TextureDefinition definition) { - _textures[new(definition.Set, definition.Binding)] = definition; + _textures[definition.Binding] = definition; } public void AddOrUpdateImage(TextureDefinition definition) { - _images[new(definition.Set, definition.Binding)] = definition; + _images[definition.Binding] = definition; } public int AddLocalMemory(MemoryDefinition definition) diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 88053658d..c4ebaee73 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -169,17 +169,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AstTextureOperation GetAstTextureOperation(TextureOperation texOp) { - return new AstTextureOperation( - inst, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.Set, - texOp.Binding, - texOp.SamplerSet, - texOp.SamplerBinding, - texOp.Index, - sources); + return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.SamplerBinding, texOp.Index, sources); } int componentsCount = BitOperations.PopCount((uint)operation.Index); diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 1e387407d..d287a1aa7 100644 --- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -4,7 +4,6 @@ namespace Ryujinx.Graphics.Shader { // New fields should be added to the end of the struct to keep disk shader cache compatibility. - public readonly int Set; public readonly int Binding; public readonly SamplerType Type; @@ -19,7 +18,6 @@ namespace Ryujinx.Graphics.Shader public readonly TextureUsageFlags Flags; public TextureDescriptor( - int set, int binding, SamplerType type, TextureFormat format, @@ -29,7 +27,6 @@ namespace Ryujinx.Graphics.Shader bool separate, TextureUsageFlags flags) { - Set = set; Binding = binding; Type = type; Format = format; diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 5e07b39f1..e1157eea4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -124,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation this.TextureSample( SamplerType.TextureBuffer, TextureFlags.IntCoords, - ResourceManager.Reservations.GetIndexBufferTextureSetAndBinding(), + ResourceManager.Reservations.IndexBufferTextureBinding, 1, new[] { vertexIndexVr }, new[] { this.IAdd(ibBaseOffset, outputVertexOffset) }); @@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation this.TextureSample( SamplerType.TextureBuffer, TextureFlags.IntCoords, - ResourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(), + ResourceManager.Reservations.TopologyRemapBufferTextureBinding, 1, new[] { vertexIndex }, new[] { this.IAdd(baseVertex, Const(index)) }); diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 5bdbb0025..9e314c620 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -618,21 +618,12 @@ namespace Ryujinx.Graphics.Shader.Translation SamplerType type, TextureFormat format, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, Operand[] sources) { Operand dest = Local(); - context.Add(new TextureOperation( - Instruction.ImageAtomic, - type, - format, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - 0, - new[] { dest }, - sources)); + context.Add(new TextureOperation(Instruction.ImageAtomic, type, format, flags, binding, 0, new[] { dest }, sources)); return dest; } @@ -642,21 +633,12 @@ namespace Ryujinx.Graphics.Shader.Translation SamplerType type, TextureFormat format, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, int compMask, Operand[] dests, Operand[] sources) { - context.Add(new TextureOperation( - Instruction.ImageLoad, - type, - format, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - compMask, - dests, - sources)); + context.Add(new TextureOperation(Instruction.ImageLoad, type, format, flags, binding, compMask, dests, sources)); } public static void ImageStore( @@ -664,19 +646,10 @@ namespace Ryujinx.Graphics.Shader.Translation SamplerType type, TextureFormat format, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, Operand[] sources) { - context.Add(new TextureOperation( - Instruction.ImageStore, - type, - format, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - 0, - null, - sources)); + context.Add(new TextureOperation(Instruction.ImageStore, type, format, flags, binding, 0, null, sources)); } public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) @@ -745,22 +718,13 @@ namespace Ryujinx.Graphics.Shader.Translation this EmitterContext context, SamplerType type, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, int compIndex, Operand[] sources) { Operand dest = Local(); - context.Add(new TextureOperation( - Instruction.Lod, - type, - TextureFormat.Unknown, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - compIndex, - new[] { dest }, - sources)); + context.Add(new TextureOperation(Instruction.Lod, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); return dest; } @@ -925,42 +889,24 @@ namespace Ryujinx.Graphics.Shader.Translation this EmitterContext context, SamplerType type, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, int compMask, Operand[] dests, Operand[] sources) { - context.Add(new TextureOperation( - Instruction.TextureSample, - type, - TextureFormat.Unknown, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - compMask, - dests, - sources)); + context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources)); } public static Operand TextureQuerySamples( this EmitterContext context, SamplerType type, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, Operand[] sources) { Operand dest = Local(); - context.Add(new TextureOperation( - Instruction.TextureQuerySamples, - type, - TextureFormat.Unknown, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - 0, - new[] { dest }, - sources)); + context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources)); return dest; } @@ -969,22 +915,13 @@ namespace Ryujinx.Graphics.Shader.Translation this EmitterContext context, SamplerType type, TextureFlags flags, - SetBindingPair setAndBinding, + int binding, int compIndex, Operand[] sources) { Operand dest = Local(); - context.Add(new TextureOperation( - Instruction.TextureQuerySize, - type, - TextureFormat.Unknown, - flags, - setAndBinding.SetIndex, - setAndBinding.Binding, - compIndex, - new[] { dest }, - sources)); + context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); return dest; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs index 11fe6599d..2523272b0 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs @@ -8,7 +8,6 @@ namespace Ryujinx.Graphics.Shader.Translation public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsShaderBallot; public readonly bool SupportsShaderBarrierDivergence; - public readonly bool SupportsShaderFloat64; public readonly bool SupportsTextureShadowLod; public readonly bool SupportsViewportMask; @@ -19,7 +18,6 @@ namespace Ryujinx.Graphics.Shader.Translation bool supportsGeometryShaderPassthrough, bool supportsShaderBallot, bool supportsShaderBarrierDivergence, - bool supportsShaderFloat64, bool supportsTextureShadowLod, bool supportsViewportMask) { @@ -29,7 +27,6 @@ namespace Ryujinx.Graphics.Shader.Translation SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsShaderBallot = supportsShaderBallot; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; - SupportsShaderFloat64 = supportsShaderFloat64; SupportsTextureShadowLod = supportsTextureShadowLod; SupportsViewportMask = supportsViewportMask; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 1f2f79a2d..4128af241 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -38,12 +38,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // If we can't do bindless elimination, remove the texture operation. // Set any destination variables to zero. - string typeName = texOp.Inst.IsImage() - ? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()) - : texOp.Type.ToGlslTextureType(); - - gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\"."); - for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++) { block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0))); @@ -68,22 +62,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return false; } - Operand bindlessHandle = texOp.GetSource(0); + Operand nvHandle = texOp.GetSource(0); - if (bindlessHandle.AsgOp is PhiNode phi) + if (nvHandle.AsgOp is not Operation handleOp || + handleOp.Inst != Instruction.Load || + (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer)) { - for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++) - { - Operand phiSource = phi.GetSource(srcIndex); + // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer. + // This is an artificial limitation to prevent it from being used in cases where it + // would have a large performance impact of loading all textures in the pool. + // It might be removed in the future, if we can mitigate the performance impact. - if (phiSource.AsgOp is not PhiNode && !IsBindlessAccessAllowed(phiSource)) - { - return false; - } - } - } - else if (!IsBindlessAccessAllowed(bindlessHandle)) - { return false; } @@ -91,8 +80,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand samplerHandle = OperandHelper.Local(); Operand textureIndex = OperandHelper.Local(); - block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, bindlessHandle, OperandHelper.Const(0xfffff))); - block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, bindlessHandle, OperandHelper.Const(20))); + block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff))); + block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20))); int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool()); @@ -102,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations bool hasSampler = !texOp.Inst.IsImage(); - SetBindingPair textureSetAndBinding = resourceManager.GetTextureOrImageBinding( + int textureBinding = resourceManager.GetTextureOrImageBinding( texOp.Inst, texOp.Type, texOp.Format, @@ -122,7 +111,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations texOp.InsertSource(1, samplerIndex); - SetBindingPair samplerSetAndBinding = resourceManager.GetTextureOrImageBinding( + int samplerBinding = resourceManager.GetTextureOrImageBinding( texOp.Inst, SamplerType.None, texOp.Format, @@ -131,35 +120,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct), samplerPoolLength); - texOp.TurnIntoArray(textureSetAndBinding, samplerSetAndBinding); + texOp.TurnIntoArray(textureBinding, samplerBinding); } else { - texOp.TurnIntoArray(textureSetAndBinding); - } - - return true; - } - - private static bool IsBindlessAccessAllowed(Operand bindlessHandle) - { - if (bindlessHandle.Type == OperandType.ConstantBuffer) - { - // Bindless access with handles from constant buffer is allowed. - - return true; - } - - if (bindlessHandle.AsgOp is not Operation handleOp || - handleOp.Inst != Instruction.Load || - (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer)) - { - // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer. - // This is an artificial limitation to prevent it from being used in cases where it - // would have a large performance impact of loading all textures in the pool. - // It might be removed in the future, if we can mitigate the performance impact. - - return false; + texOp.TurnIntoArray(textureBinding); } return true; @@ -300,7 +265,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations resourceManager, gpuAccessor, texOp, - TextureHandle.PackOffsets(src0.GetCbufOffset(), (src1.Value >> 20) & 0xfff, handleType), + TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType), TextureHandle.PackSlots(src0.GetCbufSlot(), 0), rewriteSamplerType, isImage: false); @@ -480,7 +445,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding( + int binding = resourceManager.GetTextureOrImageBinding( texOp.Inst, texOp.Type, texOp.Format, @@ -488,7 +453,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations cbufSlot, cbufOffset); - texOp.SetBinding(setAndBinding); + texOp.SetBinding(binding); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs index 1e0b3b645..f2be7975d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs @@ -126,9 +126,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block); - - if (bindlessHandle.AsgOp is not Operation handleAsgOp) + if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp) { continue; } @@ -139,8 +137,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (handleAsgOp.Inst == Instruction.BitwiseOr) { - Operand src0 = Utils.FindLastOperation(handleAsgOp.GetSource(0), block); - Operand src1 = Utils.FindLastOperation(handleAsgOp.GetSource(1), block); + Operand src0 = handleAsgOp.GetSource(0); + Operand src1 = handleAsgOp.GetSource(1); if (src0.Type == OperandType.ConstantBuffer && src1.AsgOp is Operation) { @@ -223,7 +221,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static void TurnIntoArray(ResourceManager resourceManager, TextureOperation texOp, int cbufSlot, int handleIndex, int length) { - SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding( + int binding = resourceManager.GetTextureOrImageBinding( texOp.Inst, texOp.Type, texOp.Format, @@ -232,7 +230,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations handleIndex, length); - texOp.TurnIntoArray(setAndBinding); + texOp.TurnIntoArray(binding); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 1be7c5c52..49eb3a89b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -152,14 +152,18 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { // If all phi sources are the same, we can propagate it and remove the phi. - if (!Utils.AreAllSourcesTheSameOperand(phi)) + Operand firstSrc = phi.GetSource(0); + + for (int index = 1; index < phi.SourcesCount; index++) { - return false; + if (!IsSameOperand(firstSrc, phi.GetSource(index))) + { + return false; + } } // All sources are equal, we can propagate the value. - Operand firstSrc = phi.GetSource(0); Operand dest = phi.Dest; INode[] uses = dest.UseOps.ToArray(); @@ -178,6 +182,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return true; } + private static bool IsSameOperand(Operand x, Operand y) + { + if (x.Type != y.Type || x.Value != y.Value) + { + return false; + } + + // TODO: Handle Load operations with the same storage and the same constant parameters. + return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer; + } + private static bool PropagatePack(Operation packOp) { // Propagate pack source operands to uses by unpack diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs index 097c8aa88..a509fcb42 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs @@ -31,10 +31,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations TryEliminateBitwiseOr(operation); break; - case Instruction.CompareNotEqual: - TryEliminateCompareNotEqual(operation); - break; - case Instruction.ConditionalSelect: TryEliminateConditionalSelect(operation); break; @@ -178,32 +174,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private static void TryEliminateCompareNotEqual(Operation operation) - { - // Comparison instruction returns 0 if the result is false, and -1 if true. - // Doing a not equal zero comparison on the result is redundant, so we can just copy the first result in this case. - - Operand lhs = operation.GetSource(0); - Operand rhs = operation.GetSource(1); - - if (lhs.Type == OperandType.Constant) - { - (lhs, rhs) = (rhs, lhs); - } - - if (rhs.Type != OperandType.Constant || rhs.Value != 0) - { - return; - } - - if (lhs.AsgOp is not Operation compareOp || !compareOp.Inst.IsComparison()) - { - return; - } - - operation.TurnIntoCopy(lhs); - } - private static void TryEliminateConditionalSelect(Operation operation) { Operand cond = operation.GetSource(0); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index 6ec90fa3c..74a6d5a1e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs @@ -34,50 +34,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex; } - private static bool IsSameOperand(Operand x, Operand y) - { - if (x.Type != y.Type || x.Value != y.Value) - { - return false; - } - - // TODO: Handle Load operations with the same storage and the same constant parameters. - return x == y || x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer; - } - - private static bool AreAllSourcesEqual(INode node, INode otherNode) - { - if (node.SourcesCount != otherNode.SourcesCount) - { - return false; - } - - for (int index = 0; index < node.SourcesCount; index++) - { - if (!IsSameOperand(node.GetSource(index), otherNode.GetSource(index))) - { - return false; - } - } - - return true; - } - - public static bool AreAllSourcesTheSameOperand(INode node) - { - Operand firstSrc = node.GetSource(0); - - for (int index = 1; index < node.SourcesCount; index++) - { - if (!IsSameOperand(firstSrc, node.GetSource(index))) - { - return false; - } - } - - return true; - } - private static Operation FindBranchSource(BasicBlock block) { foreach (BasicBlock sourceBlock in block.Predecessors) @@ -99,19 +55,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return inst == Instruction.BranchIfFalse || inst == Instruction.BranchIfTrue; } - private static bool IsSameCondition(Operand currentCondition, Operand queryCondition) - { - if (currentCondition == queryCondition) - { - return true; - } - - return currentCondition.AsgOp is Operation currentOperation && - queryCondition.AsgOp is Operation queryOperation && - currentOperation.Inst == queryOperation.Inst && - AreAllSourcesEqual(currentOperation, queryOperation); - } - private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock) { // Check if all the conditions for the query block are satisfied by the current block. @@ -127,10 +70,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return currentBranch != null && queryBranch != null && currentBranch.Inst == queryBranch.Inst && - IsSameCondition(currentCondition, queryCondition); + currentCondition == queryCondition; } - public static Operand FindLastOperation(Operand source, BasicBlock block, bool recurse = true) + public static Operand FindLastOperation(Operand source, BasicBlock block) { if (source.AsgOp is PhiNode phiNode) { @@ -138,48 +81,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // Ensure that conditions met for that branch are also met for the current one. // Prefer the latest sources for the phi node. - int undefCount = 0; - for (int i = phiNode.SourcesCount - 1; i >= 0; i--) { BasicBlock phiBlock = phiNode.GetBlock(i); - Operand phiSource = phiNode.GetSource(i); if (BlockConditionsMatch(block, phiBlock)) { - return phiSource; - } - else if (recurse && phiSource.AsgOp is PhiNode) - { - // Phi source is another phi. - // Let's check if that phi has a block that matches our condition. - - Operand match = FindLastOperation(phiSource, block, false); - - if (match != phiSource) - { - return match; - } - } - else if (phiSource.Type == OperandType.Undefined) - { - undefCount++; - } - } - - // If all sources but one are undefined, we can assume that the one - // that is not undefined is the right one. - - if (undefCount == phiNode.SourcesCount - 1) - { - for (int i = phiNode.SourcesCount - 1; i >= 0; i--) - { - Operand phiSource = phiNode.GetSource(i); - - if (phiSource.Type != OperandType.Undefined) - { - return phiSource; - } + return phiNode.GetSource(i); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs b/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs index 1c724223c..e27e47070 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs @@ -155,14 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index]; } - for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++) + if (operation.Dest != null && operation.Dest.Type == OperandType.Register) { - Operand dest = operation.GetDest(dstIndex); - - if (dest != null && dest.Type == OperandType.Register) - { - localOutputs[block.Index] |= GetMask(dest.GetRegister()); - } + localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister()); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 94691a5b4..890501c91 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -20,8 +20,8 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly ShaderStage _stage; private readonly string _stagePrefix; - private readonly SetBindingPair[] _cbSlotToBindingMap; - private readonly SetBindingPair[] _sbSlotToBindingMap; + private readonly int[] _cbSlotToBindingMap; + private readonly int[] _sbSlotToBindingMap; private uint _sbSlotWritten; private readonly Dictionary _sbSlots; @@ -33,7 +33,6 @@ namespace Ryujinx.Graphics.Shader.Translation private struct TextureMeta { - public int Set; public int Binding; public bool AccurateType; public SamplerType Type; @@ -65,10 +64,10 @@ namespace Ryujinx.Graphics.Shader.Translation _stage = stage; _stagePrefix = GetShaderStagePrefix(stage); - _cbSlotToBindingMap = new SetBindingPair[18]; - _sbSlotToBindingMap = new SetBindingPair[16]; - _cbSlotToBindingMap.AsSpan().Fill(new(-1, -1)); - _sbSlotToBindingMap.AsSpan().Fill(new(-1, -1)); + _cbSlotToBindingMap = new int[18]; + _sbSlotToBindingMap = new int[16]; + _cbSlotToBindingMap.AsSpan().Fill(-1); + _sbSlotToBindingMap.AsSpan().Fill(-1); _sbSlots = new(); _sbSlotsReverse = new(); @@ -147,16 +146,16 @@ namespace Ryujinx.Graphics.Shader.Translation public int GetConstantBufferBinding(int slot) { - SetBindingPair setAndBinding = _cbSlotToBindingMap[slot]; - if (setAndBinding.Binding < 0) + int binding = _cbSlotToBindingMap[slot]; + if (binding < 0) { - setAndBinding = _gpuAccessor.CreateConstantBufferBinding(slot); - _cbSlotToBindingMap[slot] = setAndBinding; + binding = _gpuAccessor.CreateConstantBufferBinding(slot); + _cbSlotToBindingMap[slot] = binding; string slotNumber = slot.ToString(CultureInfo.InvariantCulture); - AddNewConstantBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_c{slotNumber}"); + AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}"); } - return setAndBinding.Binding; + return binding; } public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding) @@ -167,14 +166,14 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } - SetBindingPair setAndBinding = _sbSlotToBindingMap[slot]; + binding = _sbSlotToBindingMap[slot]; - if (setAndBinding.Binding < 0) + if (binding < 0) { - setAndBinding = _gpuAccessor.CreateStorageBufferBinding(slot); - _sbSlotToBindingMap[slot] = setAndBinding; + binding = _gpuAccessor.CreateStorageBufferBinding(slot); + _sbSlotToBindingMap[slot] = binding; string slotNumber = slot.ToString(CultureInfo.InvariantCulture); - AddNewStorageBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_s{slotNumber}"); + AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}"); } if (write) @@ -182,7 +181,6 @@ namespace Ryujinx.Graphics.Shader.Translation _sbSlotWritten |= 1u << slot; } - binding = setAndBinding.Binding; return true; } @@ -210,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.Translation { for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++) { - if (_cbSlotToBindingMap[slot].Binding == binding) + if (_cbSlotToBindingMap[slot] == binding) { return true; } @@ -220,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } - public SetBindingPair GetTextureOrImageBinding( + public int GetTextureOrImageBinding( Instruction inst, SamplerType type, TextureFormat format, @@ -242,7 +240,7 @@ namespace Ryujinx.Graphics.Shader.Translation format = TextureFormat.Unknown; } - SetBindingPair setAndBinding = GetTextureOrImageBinding( + int binding = GetTextureOrImageBinding( cbufSlot, handle, arrayLength, @@ -257,10 +255,10 @@ namespace Ryujinx.Graphics.Shader.Translation _gpuAccessor.RegisterTexture(handle, cbufSlot); - return setAndBinding; + return binding; } - private SetBindingPair GetTextureOrImageBinding( + private int GetTextureOrImageBinding( int cbufSlot, int handle, int arrayLength, @@ -313,38 +311,21 @@ namespace Ryujinx.Graphics.Shader.Translation UsageFlags = usageFlags, }; - int setIndex; int binding; if (dict.TryGetValue(info, out var existingMeta)) { dict[info] = MergeTextureMeta(meta, existingMeta); - setIndex = existingMeta.Set; binding = existingMeta.Binding; } else { - if (arrayLength > 1 && (setIndex = _gpuAccessor.CreateExtraSet()) >= 0) - { - // We reserved an "extra set" for the array. - // In this case the binding is always the first one (0). - // Using separate sets for array is better as we need to do less descriptor set updates. + bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer; - binding = 0; - } - else - { - bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer; + binding = isImage + ? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer) + : _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer); - SetBindingPair setAndBinding = isImage - ? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer) - : _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer); - - setIndex = setAndBinding.SetIndex; - binding = setAndBinding.Binding; - } - - meta.Set = setIndex; meta.Binding = binding; dict.Add(info, meta); @@ -374,7 +355,7 @@ namespace Ryujinx.Graphics.Shader.Translation } var definition = new TextureDefinition( - setIndex, + isImage ? 3 : 2, binding, arrayLength, separate, @@ -392,12 +373,11 @@ namespace Ryujinx.Graphics.Shader.Translation Properties.AddOrUpdateTexture(definition); } - return new SetBindingPair(setIndex, binding); + return binding; } private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta) { - meta.Set = existingMeta.Set; meta.Binding = existingMeta.Binding; meta.UsageFlags |= existingMeta.UsageFlags; @@ -460,11 +440,11 @@ namespace Ryujinx.Graphics.Shader.Translation for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++) { - SetBindingPair setAndBinding = _cbSlotToBindingMap[slot]; + int binding = _cbSlotToBindingMap[slot]; - if (setAndBinding.Binding >= 0 && _usedConstantBufferBindings.Contains(setAndBinding.Binding)) + if (binding >= 0 && _usedConstantBufferBindings.Contains(binding)) { - descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot); + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot); } } @@ -484,13 +464,13 @@ namespace Ryujinx.Graphics.Shader.Translation foreach ((int key, int slot) in _sbSlots) { - SetBindingPair setAndBinding = _sbSlotToBindingMap[slot]; + int binding = _sbSlotToBindingMap[slot]; - if (setAndBinding.Binding >= 0) + if (binding >= 0) { (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None; - descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot, sbCbSlot, sbCbOffset, flags); + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags); } } @@ -527,7 +507,6 @@ namespace Ryujinx.Graphics.Shader.Translation } descriptors.Add(new TextureDescriptor( - meta.Set, meta.Binding, meta.Type, info.Format, @@ -548,7 +527,6 @@ namespace Ryujinx.Graphics.Shader.Translation } descriptors.Add(new TextureDescriptor( - meta.Set, meta.Binding, meta.Type, info.Format, @@ -609,24 +587,24 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } - private void AddNewConstantBuffer(int setIndex, int binding, string name) + private void AddNewConstantBuffer(int binding, string name) { StructureType type = new(new[] { new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), }); - Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type)); + Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, binding, name, type)); } - private void AddNewStorageBuffer(int setIndex, int binding, string name) + private void AddNewStorageBuffer(int binding, string name) { StructureType type = new(new[] { new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), }); - Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); + Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, 1, binding, name, type)); } public static string GetShaderStagePrefix(ShaderStage stage) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceReservations.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceReservations.cs index c89c4d0b6..d559f6699 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceReservations.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceReservations.cs @@ -11,8 +11,6 @@ namespace Ryujinx.Graphics.Shader.Translation public const int MaxVertexBufferTextures = 32; - private const int TextureSetIndex = 2; // TODO: Get from GPU accessor. - public int VertexInfoConstantBufferBinding { get; } public int VertexOutputStorageBufferBinding { get; } public int GeometryVertexOutputStorageBufferBinding { get; } @@ -165,21 +163,6 @@ namespace Ryujinx.Graphics.Shader.Translation return _vertexBufferTextureBaseBinding + vaLocation; } - public SetBindingPair GetVertexBufferTextureSetAndBinding(int vaLocation) - { - return new SetBindingPair(TextureSetIndex, GetVertexBufferTextureBinding(vaLocation)); - } - - public SetBindingPair GetIndexBufferTextureSetAndBinding() - { - return new SetBindingPair(TextureSetIndex, IndexBufferTextureBinding); - } - - public SetBindingPair GetTopologyRemapBufferTextureSetAndBinding() - { - return new SetBindingPair(TextureSetIndex, TopologyRemapBufferTextureBinding); - } - internal bool TryGetOffset(StorageKind storageKind, int location, int component, out int offset) { return _offsets.TryGetValue(new IoDefinition(storageKind, IoVariable.UserDefined, location, component), out offset); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs index 6ba8cb44a..072b45695 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs @@ -182,7 +182,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags, - texOp.Set, texOp.Binding, index, new[] { coordSize }, @@ -252,7 +251,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags, - texOp.Set, texOp.Binding, index, new[] { coordSize }, @@ -473,7 +471,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.Set, texOp.Binding, 1 << 3, // W component: i=0, j=0 new[] { dests[destIndex++] }, @@ -530,7 +527,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.Set, texOp.Binding, componentIndex, dests, @@ -577,7 +573,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags, - texOp.Set, texOp.Binding, index, new[] { texSizes[index] }, @@ -608,7 +603,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags, - texOp.Set, texOp.Binding, 0, new[] { lod }, @@ -639,7 +633,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Type, texOp.Format, texOp.Flags, - texOp.Set, texOp.Binding, index, new[] { texSizes[index] }, diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/VertexToCompute.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/VertexToCompute.cs index ddd2134d2..d71ada865 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/VertexToCompute.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/VertexToCompute.cs @@ -54,7 +54,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms { bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location); - SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); Operand temp = needsSextNorm ? Local() : dest; Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0); @@ -63,8 +62,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms SamplerType.TextureBuffer, TextureFormat.Unknown, TextureFlags.IntCoords, - setAndBinding.SetIndex, - setAndBinding.Binding, + context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location), 1 << component, new[] { temp }, new[] { vertexElemOffset })); @@ -77,7 +75,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } else { - SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); Operand temp = component > 0 ? Local() : dest; Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component); @@ -86,8 +83,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms SamplerType.TextureBuffer, TextureFormat.Unknown, TextureFlags.IntCoords, - setAndBinding.SetIndex, - setAndBinding.Binding, + context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location), 1, new[] { temp }, new[] { vertexElemOffset })); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index d1fbca0eb..6a31ea2e7 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (stage == ShaderStage.Vertex) { - InitializeVertexOutputs(context); + InitializePositionOutput(context); } UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents; @@ -236,20 +236,12 @@ namespace Ryujinx.Graphics.Shader.Translation } } - private static void InitializeVertexOutputs(EmitterContext context) + private static void InitializePositionOutput(EmitterContext context) { for (int c = 0; c < 4; c++) { context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f)); } - - if (context.Program.ClipDistancesWritten != 0) - { - for (int i = 0; i < 8; i++) - { - context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f)); - } - } } private static void InitializeOutput(EmitterContext context, int location, bool perPatch) diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index a579433f9..106535588 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -363,7 +363,6 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(), GpuAccessor.QueryHostSupportsShaderBallot(), GpuAccessor.QueryHostSupportsShaderBarrierDivergence(), - GpuAccessor.QueryHostSupportsShaderFloat64(), GpuAccessor.QueryHostSupportsTextureShadowLod(), GpuAccessor.QueryHostSupportsViewportMask()); @@ -413,8 +412,8 @@ namespace Ryujinx.Graphics.Shader.Translation if (Stage == ShaderStage.Vertex) { - SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding(); - TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer); + int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding; + TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(indexBuffer); int inputMap = _program.AttributeUsage.UsedInputAttributes; @@ -422,8 +421,8 @@ namespace Ryujinx.Graphics.Shader.Translation while (inputMap != 0) { int location = BitOperations.TrailingZeroCount(inputMap); - SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); - TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer); + int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location); + TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(vaBuffer); inputMap &= ~(1 << location); @@ -431,8 +430,8 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (Stage == ShaderStage.Geometry) { - SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(); - TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer); + int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding; + TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer); resourceManager.Properties.AddOrUpdateTexture(remapBuffer); int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 92e39d2e0..3f65e1225 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using System; +using System.Buffers; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -292,9 +293,9 @@ namespace Ryujinx.Graphics.Texture.Astc int depth, int levels, int layers, - out MemoryOwner decoded) + out IMemoryOwner decoded) { - decoded = MemoryOwner.Rent(QueryDecompressedSize(width, height, depth, levels, layers)); + decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers)); AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers); diff --git a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs index d7b1f0fa9..eb85334a2 100644 --- a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Buffers.Binary; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -13,7 +14,7 @@ namespace Ryujinx.Graphics.Texture private const int BlockWidth = 4; private const int BlockHeight = 4; - public static MemoryOwner DecodeBC1(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC1(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { int size = 0; @@ -22,12 +23,12 @@ namespace Ryujinx.Graphics.Texture size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; } - MemoryOwner output = MemoryOwner.Rent(size); + IMemoryOwner output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output.Span); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -101,7 +102,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC2(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC2(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { int size = 0; @@ -110,12 +111,12 @@ namespace Ryujinx.Graphics.Texture size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; } - MemoryOwner output = MemoryOwner.Rent(size); + IMemoryOwner output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output.Span); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -196,7 +197,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC3(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC3(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { int size = 0; @@ -205,13 +206,13 @@ namespace Ryujinx.Graphics.Texture size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; } - MemoryOwner output = MemoryOwner.Rent(size); + IMemoryOwner output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span rPal = stackalloc byte[8]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output.Span); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -293,7 +294,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC4(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC4(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) { int size = 0; @@ -305,8 +306,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); - MemoryOwner output = MemoryOwner.Rent(size); - Span outputSpan = output.Span; + IMemoryOwner output = ByteMemoryPool.Rent(size); + Span outputSpan = output.Memory.Span; ReadOnlySpan data64 = MemoryMarshal.Cast(data); @@ -401,7 +402,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC5(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC5(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) { int size = 0; @@ -413,7 +414,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); - MemoryOwner output = MemoryOwner.Rent(size); + IMemoryOwner output = ByteMemoryPool.Rent(size); ReadOnlySpan data64 = MemoryMarshal.Cast(data); @@ -422,7 +423,7 @@ namespace Ryujinx.Graphics.Texture Span rPal = stackalloc byte[8]; Span gPal = stackalloc byte[8]; - Span outputAsUshort = MemoryMarshal.Cast(output.Span); + Span outputAsUshort = MemoryMarshal.Cast(output.Memory.Span); Span rTileAsUint = MemoryMarshal.Cast(rTile); Span gTileAsUint = MemoryMarshal.Cast(gTile); @@ -526,7 +527,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC6(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC6(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) { int size = 0; @@ -535,8 +536,8 @@ namespace Ryujinx.Graphics.Texture size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8; } - MemoryOwner output = MemoryOwner.Rent(size); - Span outputSpan = output.Span; + IMemoryOwner output = ByteMemoryPool.Rent(size); + Span outputSpan = output.Memory.Span; int inputOffset = 0; int outputOffset = 0; @@ -565,7 +566,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeBC7(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC7(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { int size = 0; @@ -574,8 +575,8 @@ namespace Ryujinx.Graphics.Texture size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; } - MemoryOwner output = MemoryOwner.Rent(size); - Span outputSpan = output.Span; + IMemoryOwner output = ByteMemoryPool.Rent(size); + Span outputSpan = output.Memory.Span; int inputOffset = 0; int outputOffset = 0; diff --git a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs index 4db8a182b..253ba305c 100644 --- a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Common.Memory; using Ryujinx.Graphics.Texture.Encoders; using System; +using System.Buffers; namespace Ryujinx.Graphics.Texture { @@ -10,7 +11,7 @@ namespace Ryujinx.Graphics.Texture private const int BlockWidth = 4; private const int BlockHeight = 4; - public static MemoryOwner EncodeBC7(Memory data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner EncodeBC7(Memory data, int width, int height, int depth, int levels, int layers) { int size = 0; @@ -22,7 +23,7 @@ namespace Ryujinx.Graphics.Texture size += w * h * 16 * Math.Max(1, depth >> l) * layers; } - MemoryOwner output = MemoryOwner.Rent(size); + IMemoryOwner output = ByteMemoryPool.Rent(size); Memory outputMemory = output.Memory; int imageBaseIOffs = 0; diff --git a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs index 49e7154c8..52801ff47 100644 --- a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Buffers.Binary; using System.Runtime.InteropServices; @@ -50,15 +51,15 @@ namespace Ryujinx.Graphics.Texture new int[] { -3, -5, -7, -9, 2, 4, 6, 8 }, }; - public static MemoryOwner DecodeRgb(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeRgb(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - MemoryOwner output = MemoryOwner.Rent(CalculateOutputSize(width, height, depth, levels, layers)); + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output.Span); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; @@ -112,15 +113,15 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodePta(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodePta(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - MemoryOwner output = MemoryOwner.Rent(CalculateOutputSize(width, height, depth, levels, layers)); + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output.Span); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; @@ -169,15 +170,15 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner DecodeRgba(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeRgba(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - MemoryOwner output = MemoryOwner.Rent(CalculateOutputSize(width, height, depth, levels, layers)); + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output.Span); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; diff --git a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs index 5426af205..d6732674b 100644 --- a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Runtime.Intrinsics; using static Ryujinx.Graphics.Texture.BlockLinearConstants; @@ -94,7 +95,7 @@ namespace Ryujinx.Graphics.Texture }; } - public static MemoryOwner ConvertBlockLinearToLinear( + public static IMemoryOwner ConvertBlockLinearToLinear( int width, int height, int depth, @@ -120,8 +121,8 @@ namespace Ryujinx.Graphics.Texture blockHeight, bytesPerPixel); - MemoryOwner outputOwner = MemoryOwner.Rent(outSize); - Span output = outputOwner.Span; + IMemoryOwner outputOwner = ByteMemoryPool.Rent(outSize); + Span output = outputOwner.Memory.Span; int outOffs = 0; @@ -248,7 +249,7 @@ namespace Ryujinx.Graphics.Texture return outputOwner; } - public static MemoryOwner ConvertLinearStridedToLinear( + public static IMemoryOwner ConvertLinearStridedToLinear( int width, int height, int blockWidth, @@ -264,8 +265,8 @@ namespace Ryujinx.Graphics.Texture int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); lineSize = Math.Min(lineSize, outStride); - MemoryOwner output = MemoryOwner.Rent(h * outStride); - Span outSpan = output.Span; + IMemoryOwner output = ByteMemoryPool.Rent(h * outStride); + Span outSpan = output.Memory.Span; int outOffs = 0; int inOffs = 0; diff --git a/src/Ryujinx.Graphics.Texture/PixelConverter.cs b/src/Ryujinx.Graphics.Texture/PixelConverter.cs index 3676d9199..4475cc98a 100644 --- a/src/Ryujinx.Graphics.Texture/PixelConverter.cs +++ b/src/Ryujinx.Graphics.Texture/PixelConverter.cs @@ -1,6 +1,7 @@ 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; @@ -20,14 +21,13 @@ namespace Ryujinx.Graphics.Texture return (remainder, outRemainder, length / stride); } - public unsafe static MemoryOwner ConvertR4G4ToR4G4B4A4(ReadOnlySpan data, int width) + public unsafe static IMemoryOwner ConvertR4G4ToR4G4B4A4(ReadOnlySpan data, int width) { - MemoryOwner output = MemoryOwner.Rent(data.Length * 2); - Span outputSpan = output.Span; + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2); - Span outputSpanUInt16 = MemoryMarshal.Cast(outputSpan); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); if (remainder == 0) { @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture int sizeTrunc = data.Length & ~7; start = sizeTrunc; - fixed (byte* inputPtr = data, outputPtr = outputSpan) + fixed (byte* inputPtr = data, outputPtr = output.Memory.Span) { 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++) { - outputSpanUInt16[i] = data[i]; + outputSpan[i] = data[i]; } } else @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture { for (int x = 0; x < width; x++) { - outputSpanUInt16[outOffset++] = data[offset++]; + outputSpan[outOffset++] = data[offset++]; } offset += remainder; @@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan data, int width) + public static IMemoryOwner ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan data, int width) { - MemoryOwner output = MemoryOwner.Rent(data.Length * 2); + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); int offset = 0; int outOffset = 0; (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); ReadOnlySpan inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output.Span); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); for (int y = 0; y < height; y++) { @@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan data, int width, bool forceAlpha) + public static IMemoryOwner ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan data, int width, bool forceAlpha) { - MemoryOwner output = MemoryOwner.Rent(data.Length * 2); + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); int offset = 0; int outOffset = 0; (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); ReadOnlySpan inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output.Span); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); for (int y = 0; y < height; y++) { @@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan data, int width) + public static IMemoryOwner ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan data, int width) { - MemoryOwner output = MemoryOwner.Rent(data.Length * 2); + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); int offset = 0; int outOffset = 0; (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); ReadOnlySpan inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output.Span); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); for (int y = 0; y < height; y++) { @@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture return output; } - public static MemoryOwner ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan data, int width) + public static IMemoryOwner ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan data, int width) { - MemoryOwner output = MemoryOwner.Rent(data.Length * 2); + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); int offset = 0; int outOffset = 0; (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); ReadOnlySpan inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output.Span); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); for (int y = 0; y < height; y++) { diff --git a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs index 0290987fd..24e600a26 100644 --- a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs @@ -29,14 +29,7 @@ namespace Ryujinx.Graphics.Vulkan lock (queueLock) { - _pool = new CommandBufferPool( - _gd.Api, - _device, - queue, - queueLock, - _gd.QueueFamilyIndex, - _gd.IsQualcommProprietary, - isLight: true); + _pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true); } } diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs index bcfb3dbfe..24642af2d 100644 --- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs +++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs @@ -1,7 +1,6 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.Vulkan { @@ -9,55 +8,22 @@ namespace Ryujinx.Graphics.Vulkan { private const int MaxBarriersPerCall = 16; - private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit; - private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit; - private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit; - private readonly VulkanRenderer _gd; private readonly NativeArray _memoryBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray _bufferBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray _imageBarrierBatch = new(MaxBarriersPerCall); - private readonly List> _memoryBarriers = new(); - private readonly List> _bufferBarriers = new(); - private readonly List> _imageBarriers = new(); + private readonly List> _memoryBarriers = new(); + private readonly List> _bufferBarriers = new(); + private readonly List> _imageBarriers = new(); private int _queuedBarrierCount; - private enum IncoherentBarrierType - { - None, - Texture, - All, - CommandBuffer - } - - private bool _feedbackLoopActive; - private PipelineStageFlags _incoherentBufferWriteStages; - private PipelineStageFlags _incoherentTextureWriteStages; - private PipelineStageFlags _extraStages; - private IncoherentBarrierType _queuedIncoherentBarrier; - private bool _queuedFeedbackLoopBarrier; - public BarrierBatch(VulkanRenderer gd) { _gd = gd; } - public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd) - { - AccessFlags access = BufferAccess; - PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit; - - if (gd.TransformFeedbackApi != null) - { - access |= AccessFlags.TransformFeedbackWriteBitExt; - stages |= PipelineStageFlags.TransformFeedbackBitExt; - } - - return (access, stages); - } - private readonly record struct StageFlags : IEquatable { public readonly PipelineStageFlags Source; @@ -70,150 +36,47 @@ namespace Ryujinx.Graphics.Vulkan } } - private readonly struct BarrierWithStageFlags where T : unmanaged + private readonly struct BarrierWithStageFlags where T : unmanaged { public readonly StageFlags Flags; public readonly T Barrier; - public readonly T2 Resource; public BarrierWithStageFlags(StageFlags flags, T barrier) { Flags = flags; Barrier = barrier; - Resource = default; } - public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource) + public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier) { Flags = new StageFlags(srcStageFlags, dstStageFlags); Barrier = barrier; - Resource = resource; } } - private void QueueBarrier(List> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged + private void QueueBarrier(List> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged { - list.Add(new BarrierWithStageFlags(srcStageFlags, dstStageFlags, barrier, resource)); + list.Add(new BarrierWithStageFlags(srcStageFlags, dstStageFlags, barrier)); _queuedBarrierCount++; } public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags); + QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags); } public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags); + QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags); } - public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) + public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags); + QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass) + public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass) { - if (_queuedIncoherentBarrier > IncoherentBarrierType.None) - { - // We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier) - bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None; - bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None; - bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture; - - if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite)) - { - AccessFlags access = BaseAccess; - - PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit; - - if (hasBufferBarrier && hasBufferWrite) - { - access |= BufferAccess; - - if (_gd.TransformFeedbackApi != null) - { - access |= AccessFlags.TransformFeedbackWriteBitExt; - stages |= PipelineStageFlags.TransformFeedbackBitExt; - } - } - - if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer) - { - access |= CommandBufferAccess; - stages |= PipelineStageFlags.DrawIndirectBit; - } - - MemoryBarrier barrier = new MemoryBarrier() - { - SType = StructureType.MemoryBarrier, - SrcAccessMask = access, - DstAccessMask = access - }; - - QueueBarrier(barrier, stages, stages); - - _incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None; - - if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture) - { - if (program != null) - { - _incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages; - } - else - { - _incoherentBufferWriteStages = PipelineStageFlags.None; - } - } - - _queuedIncoherentBarrier = IncoherentBarrierType.None; - _queuedFeedbackLoopBarrier = false; - } - else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier) - { - // Feedback loop barrier. - - MemoryBarrier barrier = new MemoryBarrier() - { - SType = StructureType.MemoryBarrier, - SrcAccessMask = AccessFlags.ShaderWriteBit, - DstAccessMask = AccessFlags.ShaderReadBit - }; - - QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit); - - _queuedFeedbackLoopBarrier = false; - } - - _feedbackLoopActive = false; - } - } - - public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) - { - Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass); - } - - public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) - { - if (program != null) - { - _incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages; - _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages; - } - - _feedbackLoopActive |= feedbackLoopActive; - - FlushMemoryBarrier(program, inRenderPass); - - if (!inRenderPass && rpHolder != null) - { - // Render pass is about to begin. Queue any fences that normally interrupt the pass. - rpHolder.InsertForcedFences(cbs); - } - while (_queuedBarrierCount > 0) { int memoryCount = 0; @@ -223,20 +86,20 @@ namespace Ryujinx.Graphics.Vulkan bool hasBarrier = false; StageFlags flags = default; - static void AddBarriers( + static void AddBarriers( Span target, ref int queuedBarrierCount, ref bool hasBarrier, ref StageFlags flags, ref int count, - List> list) where T : unmanaged + List> list) where T : unmanaged { int firstMatch = -1; int end = list.Count; for (int i = 0; i < list.Count; i++) { - BarrierWithStageFlags barrier = list[i]; + BarrierWithStageFlags barrier = list[i]; if (!hasBarrier) { @@ -299,60 +162,21 @@ namespace Ryujinx.Graphics.Vulkan } } - if (inRenderPass && _imageBarriers.Count > 0) + if (insideRenderPass) { // Image barriers queued in the batch are meant to be globally scoped, // but inside a render pass they're scoped to just the range of the render pass. // On MoltenVK, we just break the rules and always use image barrier. // On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier. - // Generally, we want to avoid this from happening in the future, so flag the texture to immediately - // emit a barrier whenever the current render pass is bound again. + // TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done, + // notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future. - bool anyIsNonAttachment = false; - - foreach (BarrierWithStageFlags barrier in _imageBarriers) + if (!_gd.IsMoltenVk) { - // If the binding is an attachment, don't add it as a forced fence. - bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource); - - if (!isAttachment) - { - rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest); - anyIsNonAttachment = true; - } - } - - if (_gd.IsTBDR) - { - if (!_gd.IsMoltenVk) - { - if (!anyIsNonAttachment) - { - // This case is a feedback loop. To prevent this from causing an absolute performance disaster, - // remove the barriers entirely. - // If this is not here, there will be a lot of single draw render passes. - // TODO: explicit handling for feedback loops, likely outside this class. - - _queuedBarrierCount -= _imageBarriers.Count; - _imageBarriers.Clear(); - } - else - { - // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available. - // Metal already has hazard tracking so MVK doesn't need this. - endRenderPass(); - inRenderPass = false; - } - } - } - else - { - // Generic pipeline memory barriers will work for desktop GPUs. - // They do require a few more access flags on the subpass dependency, though. foreach (var barrier in _imageBarriers) { - _memoryBarriers.Add(new BarrierWithStageFlags( + _memoryBarriers.Add(new BarrierWithStageFlags( barrier.Flags, new MemoryBarrier() { @@ -366,22 +190,6 @@ namespace Ryujinx.Graphics.Vulkan } } - if (inRenderPass && _memoryBarriers.Count > 0) - { - PipelineStageFlags allFlags = PipelineStageFlags.None; - - foreach (var barrier in _memoryBarriers) - { - allFlags |= barrier.Flags.Dest; - } - - if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags)) - { - endRenderPass(); - inRenderPass = false; - } - } - AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers); AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers); AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers); @@ -390,14 +198,14 @@ namespace Ryujinx.Graphics.Vulkan { PipelineStageFlags srcStageFlags = flags.Source; - if (inRenderPass) + if (insideRenderPass) { // Inside a render pass, barrier stages can only be from rasterization. srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit; } _gd.Api.CmdPipelineBarrier( - cbs.CommandBuffer, + cb, srcStageFlags, flags.Dest, 0, @@ -411,43 +219,6 @@ namespace Ryujinx.Graphics.Vulkan } } - private void QueueIncoherentBarrier(IncoherentBarrierType type) - { - if (type > _queuedIncoherentBarrier) - { - _queuedIncoherentBarrier = type; - } - - _queuedFeedbackLoopBarrier = true; - } - - public void QueueTextureBarrier() - { - QueueIncoherentBarrier(IncoherentBarrierType.Texture); - } - - public void QueueMemoryBarrier() - { - QueueIncoherentBarrier(IncoherentBarrierType.All); - } - - public void QueueCommandBufferBarrier() - { - QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer); - } - - public void EnableTfbBarriers(bool enable) - { - if (enable) - { - _extraStages |= PipelineStageFlags.TransformFeedbackBitExt; - } - else - { - _extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt; - } - } - public void Dispose() { _memoryBarrierBatch.Dispose(); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index e840fdc02..3673ee5a1 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; @@ -30,29 +31,40 @@ namespace Ryujinx.Graphics.Vulkan private readonly VulkanRenderer _gd; private readonly Device _device; - private readonly MemoryAllocation _allocation; - private readonly Auto _buffer; - private readonly Auto _allocationAuto; + private MemoryAllocation _allocation; + private Auto _buffer; + private Auto _allocationAuto; private readonly bool _allocationImported; - private readonly ulong _bufferHandle; + private ulong _bufferHandle; private CacheByRange _cachedConvertedBuffers; public int Size { get; } - private readonly IntPtr _map; + private IntPtr _map; - private readonly MultiFenceHolder _waitable; + private MultiFenceHolder _waitable; private bool _lastAccessIsWrite; - private readonly BufferAllocationType _baseType; - private readonly BufferAllocationType _activeType; + private BufferAllocationType _baseType; + private BufferAllocationType _currentType; + private bool _swapQueued; + + public BufferAllocationType DesiredType { get; private set; } + + private int _setCount; + private int _writeCount; + private int _flushCount; + private int _flushTemp; + private int _lastFlushWrite = -1; private readonly ReaderWriterLockSlim _flushLock; private FenceHolder _flushFence; private int _flushWaiting; + private List _swapActions; + private byte[] _pendingData; private BufferMirrorRangeList _pendingDataRanges; private Dictionary _mirrors; @@ -71,7 +83,8 @@ namespace Ryujinx.Graphics.Vulkan _map = allocation.HostPointer; _baseType = type; - _activeType = currentType; + _currentType = currentType; + DesiredType = currentType; _flushLock = new ReaderWriterLockSlim(); _useMirrors = gd.IsTBDR; @@ -91,7 +104,8 @@ namespace Ryujinx.Graphics.Vulkan _map = _allocation.HostPointer + offset; _baseType = type; - _activeType = currentType; + _currentType = currentType; + DesiredType = currentType; _flushLock = new ReaderWriterLockSlim(); } @@ -106,11 +120,164 @@ namespace Ryujinx.Graphics.Vulkan Size = size; _baseType = BufferAllocationType.Sparse; - _activeType = BufferAllocationType.Sparse; + _currentType = BufferAllocationType.Sparse; + DesiredType = BufferAllocationType.Sparse; _flushLock = new ReaderWriterLockSlim(); } + public bool TryBackingSwap(ref CommandBufferScoped? cbs) + { + if (_swapQueued && DesiredType != _currentType) + { + // Only swap if the buffer is not used in any queued command buffer. + bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool); + + if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReadLockHeld && (_pendingData == null || cbs != null)) + { + var currentAllocation = _allocationAuto; + var currentBuffer = _buffer; + IntPtr currentMap = _map; + + (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, false, _currentType); + + if (buffer.Handle != 0) + { + if (cbs != null) + { + ClearMirrors(cbs.Value, 0, Size); + } + + _flushLock.EnterWriteLock(); + + ClearFlushFence(); + + _waitable = new MultiFenceHolder(Size); + + _allocation = allocation; + _allocationAuto = new Auto(allocation); + _buffer = new Auto(new DisposableBuffer(_gd.Api, _device, buffer), this, _waitable, _allocationAuto); + _bufferHandle = buffer.Handle; + _map = allocation.HostPointer; + + if (_map != IntPtr.Zero && currentMap != IntPtr.Zero) + { + // Copy data directly. Readbacks don't have to wait if this is done. + + unsafe + { + new Span((void*)currentMap, Size).CopyTo(new Span((void*)_map, Size)); + } + } + else + { + cbs ??= _gd.CommandBufferPool.Rent(); + + CommandBufferScoped cbsV = cbs.Value; + + Copy(_gd, cbsV, currentBuffer, _buffer, 0, 0, Size); + + // Need to wait for the data to reach the new buffer before data can be flushed. + + _flushFence = _gd.CommandBufferPool.GetFence(cbsV.CommandBufferIndex); + _flushFence.Get(); + } + + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Converted {Size} buffer {_currentType} to {resultType}"); + + _currentType = resultType; + + if (_swapActions != null) + { + foreach (var action in _swapActions) + { + action(); + } + + _swapActions.Clear(); + } + + currentBuffer.Dispose(); + currentAllocation.Dispose(); + + _gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer); + + _flushLock.ExitWriteLock(); + } + + _swapQueued = false; + + return true; + } + + return false; + } + + _swapQueued = false; + + return true; + } + + private void ConsiderBackingSwap() + { + if (_baseType == BufferAllocationType.Auto) + { + // When flushed, wait for a bit more info to make a decision. + bool wasFlushed = _flushTemp > 0; + int multiplier = wasFlushed ? 2 : 0; + if (_writeCount >= (WriteCountThreshold << multiplier) || _setCount >= (SetCountThreshold << multiplier) || _flushCount >= (FlushCountThreshold << multiplier)) + { + if (_flushCount > 0 || _flushTemp-- > 0) + { + // Buffers that flush should ideally be mapped in host address space for easy copies. + // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). + // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. + + bool hostMappingSensitive = _gd.Vendor == Vendor.Nvidia; + bool deviceLocalMapped = Size > DeviceLocalSizeThreshold || (wasFlushed && _writeCount > _flushCount * 10 && hostMappingSensitive) || _currentType == BufferAllocationType.DeviceLocalMapped; + + DesiredType = deviceLocalMapped ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; + + // It's harder for a buffer that is flushed to revert to another type of mapping. + if (_flushCount > 0) + { + _flushTemp = 1000; + } + } + else if (_writeCount >= (WriteCountThreshold << multiplier)) + { + // Buffers that are written often should ideally be in the device local heap. (Storage buffers) + DesiredType = BufferAllocationType.DeviceLocal; + } + else if (_setCount > (SetCountThreshold << multiplier)) + { + // Buffers that have their data set often should ideally be host mapped. (Constant buffers) + DesiredType = BufferAllocationType.HostMapped; + } + + _lastFlushWrite = -1; + _flushCount = 0; + _writeCount = 0; + _setCount = 0; + } + + if (!_swapQueued && DesiredType != _currentType) + { + _swapQueued = true; + + _gd.PipelineInternal.AddBackingSwap(this); + } + } + } + + public void Pin() + { + if (_baseType == BufferAllocationType.Auto) + { + _baseType = _currentType; + } + } + public unsafe Auto CreateView(VkFormat format, int offset, int size, Action invalidateView) { var bufferViewCreateInfo = new BufferViewCreateInfo @@ -122,11 +289,21 @@ namespace Ryujinx.Graphics.Vulkan Range = (uint)size, }; - _gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); + _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); + + (_swapActions ??= new List()).Add(invalidateView); return new Auto(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer); } + public void InheritMetrics(BufferHolder other) + { + _setCount = other._setCount; + _writeCount = other._writeCount; + _flushCount = other._flushCount; + _flushTemp = other._flushTemp; + } + public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite) { // If the last access is write, we always need a barrier to be sure we will read or modify @@ -153,7 +330,7 @@ namespace Ryujinx.Graphics.Vulkan PipelineStageFlags.AllCommandsBit, DependencyFlags.DeviceGroupBit, 1, - in memoryBarrier, + memoryBarrier, 0, null, 0, @@ -246,8 +423,18 @@ namespace Ryujinx.Graphics.Vulkan { if (isWrite) { + _writeCount++; + SignalWrite(0, Size); } + else if (isSSBO) + { + // Always consider SSBO access for swapping to device local memory. + + _writeCount++; + + ConsiderBackingSwap(); + } return _buffer; } @@ -256,6 +443,8 @@ namespace Ryujinx.Graphics.Vulkan { if (isWrite) { + _writeCount++; + SignalWrite(offset, size); } @@ -354,6 +543,8 @@ namespace Ryujinx.Graphics.Vulkan public void SignalWrite(int offset, int size) { + ConsiderBackingSwap(); + if (offset == 0 && size == Size) { _cachedConvertedBuffers.Clear(); @@ -433,6 +624,13 @@ namespace Ryujinx.Graphics.Vulkan WaitForFlushFence(); + if (_lastFlushWrite != _writeCount) + { + // If it's on the same page as the last flush, ignore it. + _lastFlushWrite = _writeCount; + _flushCount++; + } + Span result; if (_map != IntPtr.Zero) @@ -513,7 +711,8 @@ namespace Ryujinx.Graphics.Vulkan return; } - bool allowMirror = _useMirrors && allowCbsWait && cbs != null && _activeType <= BufferAllocationType.HostMapped; + _setCount++; + bool allowMirror = _useMirrors && allowCbsWait && cbs != null && _currentType <= BufferAllocationType.HostMapped; if (_map != IntPtr.Zero) { @@ -664,6 +863,8 @@ namespace Ryujinx.Graphics.Vulkan var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length, true).Value; + _writeCount--; + InsertBufferBarrier( _gd, cbs.CommandBuffer, @@ -770,7 +971,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - in memoryBarrier, + memoryBarrier, 0, null); } @@ -899,6 +1100,8 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { + _swapQueued = false; + _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); _buffer.Dispose(); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 7523913ec..33289a0e0 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -103,19 +103,12 @@ namespace Ryujinx.Graphics.Vulkan usage |= BufferUsageFlags.IndirectBufferBit; } - var externalMemoryBuffer = new ExternalMemoryBufferCreateInfo - { - SType = StructureType.ExternalMemoryBufferCreateInfo, - HandleTypes = ExternalMemoryHandleTypeFlags.HostAllocationBitExt, - }; - var bufferCreateInfo = new BufferCreateInfo { SType = StructureType.BufferCreateInfo, Size = (ulong)size, Usage = usage, SharingMode = SharingMode.Exclusive, - PNext = &externalMemoryBuffer, }; gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); @@ -172,6 +165,10 @@ namespace Ryujinx.Graphics.Vulkan if (TryGetBuffer(range.Handle, out var existingHolder)) { + // Since this buffer now also owns the memory from the referenced buffer, + // we pin it to ensure the memory location will not change. + existingHolder.Pin(); + (var memory, var offset) = existingHolder.GetDeviceMemoryAndOffset(); memoryBinds[index] = new SparseMemoryBind() @@ -221,7 +218,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferBinds = &bufferBind }; - gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError(); + gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError(); } var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations); @@ -238,9 +235,10 @@ namespace Ryujinx.Graphics.Vulkan int size, bool sparseCompatible = false, BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default, bool forceMirrors = false) { - return CreateWithHandle(gd, size, out _, sparseCompatible, baseType, forceMirrors); + return CreateWithHandle(gd, size, out _, sparseCompatible, baseType, storageHint, forceMirrors); } public BufferHandle CreateWithHandle( @@ -249,9 +247,10 @@ namespace Ryujinx.Graphics.Vulkan out BufferHolder holder, bool sparseCompatible = false, BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default, bool forceMirrors = false) { - holder = Create(gd, size, forConditionalRendering: false, sparseCompatible, baseType); + holder = Create(gd, size, forConditionalRendering: false, sparseCompatible, baseType, storageHint); if (holder == null) { return BufferHandle.Null; @@ -388,13 +387,31 @@ namespace Ryujinx.Graphics.Vulkan int size, bool forConditionalRendering = false, bool sparseCompatible = false, - BufferAllocationType baseType = BufferAllocationType.HostMapped) + BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default) { BufferAllocationType type = baseType; + BufferHolder storageHintHolder = null; if (baseType == BufferAllocationType.Auto) { - type = BufferAllocationType.HostMapped; + if (gd.IsSharedMemory) + { + baseType = BufferAllocationType.HostMapped; + type = baseType; + } + else + { + type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped; + } + + if (storageHint != BufferHandle.Null) + { + if (TryGetBuffer(storageHint, out storageHintHolder)) + { + type = storageHintHolder.DesiredType; + } + } } (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = @@ -404,6 +421,11 @@ namespace Ryujinx.Graphics.Vulkan { var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType); + if (storageHintHolder != null) + { + holder.InheritMetrics(storageHintHolder); + } + return holder; } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs index e49df765d..d585dd53c 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs @@ -25,10 +25,7 @@ namespace Ryujinx.Graphics.Vulkan { var buffer = _buffer.Get(cbs, _offset, _size, true).Value; - ulong offset = (ulong)_offset; - ulong size = (ulong)_size; - - gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size); + gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size); } } diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index e1fd3fb9d..278dbecfa 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private readonly Queue _queue; private readonly object _queueLock; - private readonly bool _concurrentFenceWaitUnsupported; private readonly CommandPool _pool; private readonly Thread _owner; @@ -31,9 +30,11 @@ namespace Ryujinx.Graphics.Vulkan public int SubmissionCount; public CommandBuffer CommandBuffer; public FenceHolder Fence; + public SemaphoreHolder Semaphore; public List Dependants; public List Waitables; + public HashSet Dependencies; public void Initialize(Vk api, Device device, CommandPool pool) { @@ -45,10 +46,11 @@ namespace Ryujinx.Graphics.Vulkan Level = CommandBufferLevel.Primary, }; - api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer); + api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer); Dependants = new List(); Waitables = new List(); + Dependencies = new HashSet(); } } @@ -59,20 +61,12 @@ namespace Ryujinx.Graphics.Vulkan private int _queuedCount; private int _inUseCount; - public unsafe CommandBufferPool( - Vk api, - Device device, - Queue queue, - object queueLock, - uint queueFamilyIndex, - bool concurrentFenceWaitUnsupported, - bool isLight = false) + public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false) { _api = api; _device = device; _queue = queue; _queueLock = queueLock; - _concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported; _owner = Thread.CurrentThread; var commandPoolCreateInfo = new CommandPoolCreateInfo @@ -83,7 +77,7 @@ namespace Ryujinx.Graphics.Vulkan CommandPoolCreateFlags.ResetCommandBufferBit, }; - api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError(); + api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError(); // We need at least 2 command buffers to get texture data in some cases. _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers; @@ -140,6 +134,14 @@ namespace Ryujinx.Graphics.Vulkan } } + public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs) + { + Debug.Assert(_commandBuffers[cbIndex].InUse); + var semaphoreHolder = _commandBuffers[dependencyCbs.CommandBufferIndex].Semaphore; + semaphoreHolder.Get(); + _commandBuffers[cbIndex].Dependencies.Add(semaphoreHolder); + } + public void AddWaitable(int cbIndex, MultiFenceHolder waitable) { ref var entry = ref _commandBuffers[cbIndex]; @@ -253,7 +255,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.CommandBufferBeginInfo, }; - _api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError(); + _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError(); return new CommandBufferScoped(this, entry.CommandBuffer, cursor); } @@ -311,7 +313,7 @@ namespace Ryujinx.Graphics.Vulkan lock (_queueLock) { - _api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError(); + _api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError(); } } } @@ -343,13 +345,19 @@ namespace Ryujinx.Graphics.Vulkan waitable.RemoveBufferUses(cbIndex); } + foreach (var dependency in entry.Dependencies) + { + dependency.Put(); + } + entry.Dependants.Clear(); entry.Waitables.Clear(); + entry.Dependencies.Clear(); entry.Fence?.Dispose(); if (refreshFence) { - entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported); + entry.Fence = new FenceHolder(_api, _device); } else { diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs index 2accd69b2..270cdc6e6 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs @@ -26,6 +26,11 @@ namespace Ryujinx.Graphics.Vulkan _pool.AddWaitable(CommandBufferIndex, waitable); } + public void AddDependency(CommandBufferScoped dependencyCbs) + { + _pool.AddDependency(CommandBufferIndex, dependencyCbs); + } + public FenceHolder GetFence() { return _pool.GetFence(CommandBufferIndex); diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index 40fc01b24..846dd5c7d 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferInfo = &bufferInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } } @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferInfo = pBufferInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } } @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = &imageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } } @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = pImageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } } @@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = pImageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); i += count - 1; } @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan PTexelBufferView = &texelBufferView, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } } @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan PTexelBufferView = pTexelBufferView + i, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); } i += count; diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs index 97669942c..707ae1292 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan PPoolSizes = pPoolsSize, }; - Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError(); + Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 3780dc174..a0010e660 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; using System.Buffers; -using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CompareOp = Ryujinx.Graphics.GAL.CompareOp; @@ -43,15 +42,15 @@ namespace Ryujinx.Graphics.Vulkan private record struct TextureRef { public ShaderStage Stage; - public TextureView View; - public Auto ImageView; + public TextureStorage Storage; + public Auto View; public Auto Sampler; - public TextureRef(ShaderStage stage, TextureView view, Auto imageView, Auto sampler) + public TextureRef(ShaderStage stage, TextureStorage storage, Auto view, Auto sampler) { Stage = stage; + Storage = storage; View = view; - ImageView = imageView; Sampler = sampler; } } @@ -59,21 +58,32 @@ namespace Ryujinx.Graphics.Vulkan private record struct ImageRef { public ShaderStage Stage; - public TextureView View; - public Auto ImageView; + public TextureStorage Storage; + public Auto View; - public ImageRef(ShaderStage stage, TextureView view, Auto imageView) + public ImageRef(ShaderStage stage, TextureStorage storage, Auto view) { Stage = stage; + Storage = storage; View = view; - ImageView = imageView; } } - private readonly record struct ArrayRef(ShaderStage Stage, T Array); + private record struct ArrayRef + { + public ShaderStage Stage; + public T Array; + + public ArrayRef(ShaderStage stage, T array) + { + Stage = stage; + Array = array; + } + } private readonly VulkanRenderer _gd; private readonly Device _device; + private readonly PipelineBase _pipeline; private ShaderCollection _program; private readonly BufferRef[] _uniformBufferRefs; @@ -82,13 +92,11 @@ namespace Ryujinx.Graphics.Vulkan private readonly ImageRef[] _imageRefs; private readonly TextureBuffer[] _bufferTextureRefs; private readonly TextureBuffer[] _bufferImageRefs; + private readonly Format[] _bufferImageFormats; private ArrayRef[] _textureArrayRefs; private ArrayRef[] _imageArrayRefs; - private ArrayRef[] _textureArrayExtraRefs; - private ArrayRef[] _imageArrayExtraRefs; - private readonly DescriptorBufferInfo[] _uniformBuffers; private readonly DescriptorBufferInfo[] _storageBuffers; private readonly DescriptorImageInfo[] _textures; @@ -124,12 +132,11 @@ namespace Ryujinx.Graphics.Vulkan private readonly TextureView _dummyTexture; private readonly SamplerHolder _dummySampler; - public List FeedbackLoopHazards { get; private set; } - - public DescriptorSetUpdater(VulkanRenderer gd, Device device) + public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline) { _gd = gd; _device = device; + _pipeline = pipeline; // Some of the bindings counts needs to be multiplied by 2 because we have buffer and // regular textures/images interleaved on the same descriptor set. @@ -140,13 +147,11 @@ namespace Ryujinx.Graphics.Vulkan _imageRefs = new ImageRef[Constants.MaxImageBindings * 2]; _bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2]; _bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2]; + _bufferImageFormats = new Format[Constants.MaxImageBindings * 2]; _textureArrayRefs = Array.Empty>(); _imageArrayRefs = Array.Empty>(); - _textureArrayExtraRefs = Array.Empty>(); - _imageArrayExtraRefs = Array.Empty>(); - _uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings]; _storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings]; _textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage]; @@ -210,15 +215,10 @@ namespace Ryujinx.Graphics.Vulkan _templateUpdater = new(); } - public void Initialize(bool isMainPipeline) + public void Initialize() { - MemoryOwner dummyTextureData = MemoryOwner.RentCleared(4); + IMemoryOwner dummyTextureData = ByteMemoryPool.RentCleared(4); _dummyTexture.SetData(dummyTextureData); - - if (isMainPipeline) - { - FeedbackLoopHazards = new(); - } } private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) @@ -281,18 +281,6 @@ namespace Ryujinx.Graphics.Vulkan public void InsertBindingBarriers(CommandBufferScoped cbs) { - if ((FeedbackLoopHazards?.Count ?? 0) > 0) - { - // Clear existing hazards - they will be rebuilt. - - foreach (TextureView hazard in FeedbackLoopHazards) - { - hazard.DecrementHazardUses(); - } - - FeedbackLoopHazards.Clear(); - } - foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex]) { if (segment.Type == ResourceType.TextureAndSampler) @@ -302,14 +290,13 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < segment.Count; i++) { ref var texture = ref _textureRefs[segment.Binding + i]; - texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); + texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags()); } } else { - ref var arrayRef = ref _textureArrayRefs[segment.Binding]; - PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); - arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); + PipelineStageFlags stageFlags = _textureArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags(); + _textureArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags); } } } @@ -323,45 +310,13 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < segment.Count; i++) { ref var image = ref _imageRefs[segment.Binding + i]; - image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); + image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags()); } } else { - ref var arrayRef = ref _imageArrayRefs[segment.Binding]; - PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); - arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); - } - } - } - - for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < _program.BindingSegments.Length; setIndex++) - { - var bindingSegments = _program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - continue; - } - - ResourceBindingSegment segment = bindingSegments[0]; - - if (segment.IsArray) - { - if (segment.Type == ResourceType.Texture || - segment.Type == ResourceType.Sampler || - segment.Type == ResourceType.TextureAndSampler || - segment.Type == ResourceType.BufferTexture) - { - ref var arrayRef = ref _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts]; - PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); - arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); - } - else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage) - { - ref var arrayRef = ref _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts]; - PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); - arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); + PipelineStageFlags stageFlags = _imageArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags(); + _imageArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags); } } } @@ -389,25 +344,29 @@ namespace Ryujinx.Graphics.Vulkan _dirty = DirtyFlags.All; } - public void SetImage(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture image) + public void SetImage( + CommandBufferScoped cbs, + ShaderStage stage, + int binding, + ITexture image, + Format imageFormat) { if (image is TextureBuffer imageBuffer) { _bufferImageRefs[binding] = imageBuffer; + _bufferImageFormats[binding] = imageFormat; } else if (image is TextureView view) { - ref ImageRef iRef = ref _imageRefs[binding]; + view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); - iRef.View?.ClearUsage(FeedbackLoopHazards); - view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); - - iRef = new(stage, view, view.GetIdentityImageView()); + _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView()); } else { _imageRefs[binding] = default; _bufferImageRefs[binding] = null; + _bufferImageFormats[binding] = default; } SignalDirty(DirtyFlags.Image); @@ -500,12 +459,9 @@ namespace Ryujinx.Graphics.Vulkan } else if (texture is TextureView view) { - ref TextureRef iRef = ref _textureRefs[binding]; + view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); - iRef.View?.ClearUsage(FeedbackLoopHazards); - view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); - - iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); + _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); } else { @@ -527,7 +483,7 @@ namespace Ryujinx.Graphics.Vulkan { view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); - _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); + _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); SignalDirty(DirtyFlags.Texture); } @@ -539,39 +495,25 @@ namespace Ryujinx.Graphics.Vulkan public void SetTextureArray(CommandBufferScoped cbs, ShaderStage stage, int binding, ITextureArray array) { - ref ArrayRef arrayRef = ref GetArrayRef(ref _textureArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) + if (_textureArrayRefs.Length <= binding) { - arrayRef.Array?.DecrementBindCount(); - - if (array is TextureArray textureArray) - { - textureArray.IncrementBindCount(); - textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); - } - - arrayRef = new ArrayRef(stage, array as TextureArray); - - SignalDirty(DirtyFlags.Texture); + Array.Resize(ref _textureArrayRefs, binding + ArrayGrowthSize); } - } - public void SetTextureArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, ITextureArray array) - { - ref ArrayRef arrayRef = ref GetArrayRef(ref _textureArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts); - - if (arrayRef.Stage != stage || arrayRef.Array != array) + if (_textureArrayRefs[binding].Stage != stage || _textureArrayRefs[binding].Array != array) { - arrayRef.Array?.DecrementBindCount(); + if (_textureArrayRefs[binding].Array != null) + { + _textureArrayRefs[binding].Array.Bound = false; + } if (array is TextureArray textureArray) { - textureArray.IncrementBindCount(); + textureArray.Bound = true; textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); } - arrayRef = new ArrayRef(stage, array as TextureArray); + _textureArrayRefs[binding] = new ArrayRef(stage, array as TextureArray); SignalDirty(DirtyFlags.Texture); } @@ -579,56 +521,30 @@ namespace Ryujinx.Graphics.Vulkan public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array) { - ref ArrayRef arrayRef = ref GetArrayRef(ref _imageArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) + if (_imageArrayRefs.Length <= binding) { - arrayRef.Array?.DecrementBindCount(); + Array.Resize(ref _imageArrayRefs, binding + ArrayGrowthSize); + } + + if (_imageArrayRefs[binding].Stage != stage || _imageArrayRefs[binding].Array != array) + { + if (_imageArrayRefs[binding].Array != null) + { + _imageArrayRefs[binding].Array.Bound = false; + } if (array is ImageArray imageArray) { - imageArray.IncrementBindCount(); + imageArray.Bound = true; imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); } - arrayRef = new ArrayRef(stage, array as ImageArray); + _imageArrayRefs[binding] = new ArrayRef(stage, array as ImageArray); SignalDirty(DirtyFlags.Image); } } - public void SetImageArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, IImageArray array) - { - ref ArrayRef arrayRef = ref GetArrayRef(ref _imageArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef.Array?.DecrementBindCount(); - - if (array is ImageArray imageArray) - { - imageArray.IncrementBindCount(); - imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); - } - - arrayRef = new ArrayRef(stage, array as ImageArray); - - SignalDirty(DirtyFlags.Image); - } - } - - private static ref ArrayRef GetArrayRef(ref ArrayRef[] array, int index, int growthSize = 1) - { - ArgumentOutOfRangeException.ThrowIfNegative(index); - - if (array.Length <= index) - { - Array.Resize(ref array, index + growthSize); - } - - return ref array[index]; - } - public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan buffers) { for (int i = 0; i < buffers.Length; i++) @@ -678,47 +594,31 @@ namespace Ryujinx.Graphics.Vulkan return; } - var program = _program; - if (_dirty.HasFlag(DirtyFlags.Uniform)) { - if (program.UsePushDescriptors) + if (_program.UsePushDescriptors) { - UpdateAndBindUniformBufferPd(cbs); + UpdateAndBindUniformBufferPd(cbs, pbp); } else { - UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp); + UpdateAndBind(cbs, PipelineBase.UniformSetIndex, pbp); } } if (_dirty.HasFlag(DirtyFlags.Storage)) { - UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp); + UpdateAndBind(cbs, PipelineBase.StorageSetIndex, pbp); } if (_dirty.HasFlag(DirtyFlags.Texture)) { - if (program.UpdateTexturesWithoutTemplate) - { - UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp); - } - else - { - UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp); - } + UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp); } if (_dirty.HasFlag(DirtyFlags.Image)) { - UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); - } - - if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts) - { - // Program is using extra sets, we need to bind those too. - - BindExtraSets(cbs, program, pbp); + UpdateAndBind(cbs, PipelineBase.ImageSetIndex, pbp); } _dirty = DirtyFlags.None; @@ -758,8 +658,9 @@ namespace Ryujinx.Graphics.Vulkan } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp) + private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp) { + var program = _program; var bindingSegments = program.BindingSegments[setIndex]; if (bindingSegments.Length == 0) @@ -853,7 +754,7 @@ namespace Ryujinx.Graphics.Vulkan ref var texture = ref textures[i]; ref var refs = ref _textureRefs[binding + i]; - texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; + texture.ImageView = refs.View?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; if (texture.ImageView.Handle == 0) @@ -903,7 +804,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default; + images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; } tu.Push(images[..count]); @@ -914,7 +815,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default; + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default; } tu.Push(bufferImages[..count]); @@ -940,88 +841,35 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } - private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) + private unsafe void UpdateBuffers( + CommandBufferScoped cbs, + PipelineBindPoint pbp, + int baseBinding, + ReadOnlySpan bufferInfo, + DescriptorType type) { - int setIndex = PipelineBase.TextureSetIndex; - var bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) + if (bufferInfo.Length == 0) { return; } - if (_updateDescriptorCacheCbIndex) + fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo) { - _updateDescriptorCacheCbIndex = false; - program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex); - } - - var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs); - - foreach (ResourceBindingSegment segment in bindingSegments) - { - int binding = segment.Binding; - int count = segment.Count; - - if (!segment.IsArray) + var writeDescriptorSet = new WriteDescriptorSet { - if (segment.Type != ResourceType.BufferTexture) - { - Span textures = _textures; + SType = StructureType.WriteDescriptorSet, + DstBinding = (uint)baseBinding, + DescriptorType = type, + DescriptorCount = (uint)bufferInfo.Length, + PBufferInfo = pBufferInfo, + }; - for (int i = 0; i < count; i++) - { - ref var texture = ref textures[i]; - ref var refs = ref _textureRefs[binding + i]; - - texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; - texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; - - if (texture.ImageView.Handle == 0) - { - texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; - } - - if (texture.Sampler.Handle == 0) - { - texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; - } - } - - dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler); - } - else - { - Span bufferTextures = _bufferTextures; - - for (int i = 0; i < count; i++) - { - bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; - } - - dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer); - } - } - else - { - if (segment.Type != ResourceType.BufferTexture) - { - dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler); - } - else - { - dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer); - } - } + _gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet); } - - var sets = dsc.GetSets(); - - _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs) + private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) { int sequence = _pdSequence; var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; @@ -1085,56 +933,6 @@ namespace Ryujinx.Graphics.Vulkan } } - private void BindExtraSets(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) - { - for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < program.BindingSegments.Length; setIndex++) - { - var bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - continue; - } - - ResourceBindingSegment segment = bindingSegments[0]; - - if (segment.IsArray) - { - DescriptorSet[] sets = null; - - if (segment.Type == ResourceType.Texture || - segment.Type == ResourceType.Sampler || - segment.Type == ResourceType.TextureAndSampler || - segment.Type == ResourceType.BufferTexture) - { - sets = _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets( - _device, - cbs, - _templateUpdater, - program, - setIndex, - _dummyTexture, - _dummySampler); - } - else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage) - { - sets = _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets( - _device, - cbs, - _templateUpdater, - program, - setIndex, - _dummyTexture); - } - - if (sets != null) - { - _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); - } - } - } - } - public void SignalCommandBufferChange() { _updateDescriptorCacheCbIndex = true; diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/AreaScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/AreaScalingFilter.cs deleted file mode 100644 index 87b46df80..000000000 --- a/src/Ryujinx.Graphics.Vulkan/Effects/AreaScalingFilter.cs +++ /dev/null @@ -1,101 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using Silk.NET.Vulkan; -using System; -using Extent2D = Ryujinx.Graphics.GAL.Extents2D; -using Format = Silk.NET.Vulkan.Format; -using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; - -namespace Ryujinx.Graphics.Vulkan.Effects -{ - internal class AreaScalingFilter : IScalingFilter - { - private readonly VulkanRenderer _renderer; - private PipelineHelperShader _pipeline; - private ISampler _sampler; - private ShaderCollection _scalingProgram; - private Device _device; - - public float Level { get; set; } - - public AreaScalingFilter(VulkanRenderer renderer, Device device) - { - _device = device; - _renderer = renderer; - - Initialize(); - } - - public void Dispose() - { - _pipeline.Dispose(); - _scalingProgram.Dispose(); - _sampler.Dispose(); - } - - public void Initialize() - { - _pipeline = new PipelineHelperShader(_renderer, _device); - - _pipeline.Initialize(); - - var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv"); - - var scalingResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) - .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); - - _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); - - _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[] - { - new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv), - }, scalingResourceLayout); - } - - public void Run( - TextureView view, - CommandBufferScoped cbs, - Auto destinationTexture, - Format format, - int width, - int height, - Extent2D source, - Extent2D destination) - { - _pipeline.SetCommandBuffer(cbs); - _pipeline.SetProgram(_scalingProgram); - _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler); - - ReadOnlySpan dimensionsBuffer = stackalloc float[] - { - source.X1, - source.X2, - source.Y1, - source.Y2, - destination.X1, - destination.X2, - destination.Y1, - destination.Y2, - }; - - int rangeSize = dimensionsBuffer.Length * sizeof(float); - using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, dimensionsBuffer); - - int threadGroupWorkRegionDim = 16; - int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); - _pipeline.SetImage(0, destinationTexture); - _pipeline.DispatchCompute(dispatchX, dispatchY, 1); - _pipeline.ComputeBarrier(); - - _pipeline.Finish(); - } - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 080dde5e5..5a5ddf8c8 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects var scalingResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); var sharpeningResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); - _pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); + _pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 26314b7bf..c12933335 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects var resourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); - _pipeline.SetImage(ShaderStage.Compute, 0, _texture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); + _pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.glsl deleted file mode 100644 index e34dd77dd..000000000 --- a/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.glsl +++ /dev/null @@ -1,122 +0,0 @@ -// Scaling - -#version 430 core -layout (local_size_x = 16, local_size_y = 16) in; -layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput; -layout( binding = 1, set = 2) uniform sampler2D Source; -layout( binding = 2 ) uniform dimensions{ - float srcX0; - float srcX1; - float srcY0; - float srcY1; - float dstX0; - float dstX1; - float dstY0; - float dstY1; -}; - -/***** Area Sampling *****/ - -// By Sam Belliveau and Filippo Tarpini. Public Domain license. -// Effectively a more accurate sharp bilinear filter when upscaling, -// that also works as a mathematically perfect downscale filter. -// https://entropymine.com/imageworsener/pixelmixing/ -// https://github.com/obsproject/obs-studio/pull/1715 -// https://legacy.imagemagick.org/Usage/filter/ -vec4 AreaSampling(vec2 xy) -{ - // Determine the sizes of the source and target images. - vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0)); - vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0)); - vec2 inverted_target_size = vec2(1.0) / target_size; - - // Compute the top-left and bottom-right corners of the target pixel box. - vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1)); - vec2 t_end = t_beg + vec2(1.0, 1.0); - - // Convert the target pixel box to source pixel box. - vec2 beg = t_beg * inverted_target_size * source_size; - vec2 end = t_end * inverted_target_size * source_size; - - // Compute the top-left and bottom-right corners of the pixel box. - ivec2 f_beg = ivec2(beg); - ivec2 f_end = ivec2(end); - - // Compute how much of the start and end pixels are covered horizontally & vertically. - float area_w = 1.0 - fract(beg.x); - float area_n = 1.0 - fract(beg.y); - float area_e = fract(end.x); - float area_s = fract(end.y); - - // Compute the areas of the corner pixels in the pixel box. - float area_nw = area_n * area_w; - float area_ne = area_n * area_e; - float area_sw = area_s * area_w; - float area_se = area_s * area_e; - - // Initialize the color accumulator. - vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0); - - // Accumulate corner pixels. - avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0); - avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0); - avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0); - avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0); - - // Determine the size of the pixel box. - int x_range = int(f_end.x - f_beg.x - 0.5); - int y_range = int(f_end.y - f_beg.y - 0.5); - - // Accumulate top and bottom edge pixels. - for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) - { - avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0); - avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0); - } - - // Accumulate left and right edge pixels and all the pixels in between. - for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) - { - avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0); - avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0); - - for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) - { - avg_color += texelFetch(Source, ivec2(x, y), 0); - } - } - - // Compute the area of the pixel box that was sampled. - float area_corners = area_nw + area_ne + area_sw + area_se; - float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e); - float area_center = float(x_range) * float(y_range); - - // Return the normalized average color. - return avg_color / (area_corners + area_edges + area_center); -} - -float insideBox(vec2 v, vec2 bLeft, vec2 tRight) { - vec2 s = step(bLeft, v) - step(tRight, v); - return s.x * s.y; -} - -vec2 translateDest(vec2 pos) { - vec2 translatedPos = vec2(pos.x, pos.y); - translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x; - translatedPos.y = dstY0 < dstY1 ? dstY1 + dstY0 - translatedPos.y - 1 : translatedPos.y; - return translatedPos; -} - -void main() -{ - vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1); - vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0); - ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - if (insideBox(loc, bLeft, tRight) == 0) { - imageStore(imgOutput, loc, vec4(0, 0, 0, 1)); - return; - } - - vec4 outColor = AreaSampling(loc); - imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1)); -} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv deleted file mode 100644 index 7d097280f..000000000 Binary files a/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv and /dev/null differ diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index a8e68f429..08e07f256 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects var edgeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); var blendResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); var neighbourResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); @@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); - _pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); + _pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); - _pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); + _pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.Specialize(_specConstants); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); - _pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); + _pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 9d1fd9ffd..f9243bf83 100644 --- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -424,20 +424,10 @@ namespace Ryujinx.Graphics.Vulkan public static BufferAllocationType Convert(this BufferAccess access) { - BufferAccess memType = access & BufferAccess.MemoryTypeMask; - - if (memType == BufferAccess.HostMemory || access.HasFlag(BufferAccess.Stream)) + if (access.HasFlag(BufferAccess.FlushPersistent) || access.HasFlag(BufferAccess.Stream)) { return BufferAllocationType.HostMapped; } - else if (memType == BufferAccess.DeviceMemory) - { - return BufferAllocationType.DeviceLocal; - } - else if (memType == BufferAccess.DeviceMemoryMapped) - { - return BufferAllocationType.DeviceLocalMapped; - } return BufferAllocationType.Auto; } diff --git a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs deleted file mode 100644 index 22f73679d..000000000 --- a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Vulkan -{ - [Flags] - internal enum FeedbackLoopAspects - { - None = 0, - Color = 1 << 0, - Depth = 1 << 1, - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs index 0cdb93f20..4f0a87160 100644 --- a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs @@ -10,15 +10,12 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private Fence _fence; private int _referenceCount; - private int _lock; - private readonly bool _concurrentWaitUnsupported; private bool _disposed; - public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported) + public unsafe FenceHolder(Vk api, Device device) { _api = api; _device = device; - _concurrentWaitUnsupported = concurrentWaitUnsupported; var fenceCreateInfo = new FenceCreateInfo { @@ -50,11 +47,6 @@ namespace Ryujinx.Graphics.Vulkan } while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); - if (_concurrentWaitUnsupported) - { - AcquireLock(); - } - fence = _fence; return true; } @@ -65,16 +57,6 @@ namespace Ryujinx.Graphics.Vulkan return _fence; } - public void PutLock() - { - Put(); - - if (_concurrentWaitUnsupported) - { - ReleaseLock(); - } - } - public void Put() { if (Interlocked.Decrement(ref _referenceCount) == 0) @@ -84,67 +66,24 @@ namespace Ryujinx.Graphics.Vulkan } } - private void AcquireLock() - { - while (!TryAcquireLock()) - { - Thread.SpinWait(32); - } - } - - private bool TryAcquireLock() - { - return Interlocked.Exchange(ref _lock, 1) == 0; - } - - private void ReleaseLock() - { - Interlocked.Exchange(ref _lock, 0); - } - public void Wait() { - if (_concurrentWaitUnsupported) + Span fences = stackalloc Fence[] { - AcquireLock(); + _fence, + }; - try - { - FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence }); - } - finally - { - ReleaseLock(); - } - } - else - { - FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence }); - } + FenceHelper.WaitAllIndefinitely(_api, _device, fences); } public bool IsSignaled() { - if (_concurrentWaitUnsupported) + Span fences = stackalloc Fence[] { - if (!TryAcquireLock()) - { - return false; - } + _fence, + }; - try - { - return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence }); - } - finally - { - ReleaseLock(); - } - } - else - { - return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence }); - } + return FenceHelper.AllSignaled(_api, _device, fences); } public void Dispose() diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 8d80e9d05..8079e5ff9 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Vulkan public VkFormat[] AttachmentFormats { get; } public int[] AttachmentIndices { get; } public uint AttachmentIntegerFormatMask { get; } - public bool LogicOpsAllowed { get; } public int AttachmentsCount { get; } public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1; @@ -33,9 +32,7 @@ namespace Ryujinx.Graphics.Vulkan public FramebufferParams(Device device, TextureView view, uint width, uint height) { - var format = view.Info.Format; - - bool isDepthStencil = format.IsDepthOrStencil(); + bool isDepthStencil = view.Info.Format.IsDepthOrStencil(); _device = device; _attachments = new[] { view.GetImageViewForAttachment() }; @@ -59,8 +56,6 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new[] { (uint)view.Info.Samples }; AttachmentFormats = new[] { view.VkFormat }; AttachmentIndices = isDepthStencil ? Array.Empty() : new[] { 0 }; - AttachmentIntegerFormatMask = format.IsInteger() ? 1u : 0u; - LogicOpsAllowed = !format.IsFloatOrSrgb(); AttachmentsCount = 1; @@ -90,7 +85,6 @@ namespace Ryujinx.Graphics.Vulkan int index = 0; int bindIndex = 0; uint attachmentIntegerFormatMask = 0; - bool allFormatsFloatOrSrgb = colorsCount != 0; foreach (ITexture color in colors) { @@ -107,15 +101,11 @@ namespace Ryujinx.Graphics.Vulkan AttachmentFormats[index] = texture.VkFormat; AttachmentIndices[index] = bindIndex; - var format = texture.Info.Format; - - if (format.IsInteger()) + if (texture.Info.Format.IsInteger()) { attachmentIntegerFormatMask |= 1u << bindIndex; } - allFormatsFloatOrSrgb &= format.IsFloatOrSrgb(); - width = Math.Min(width, (uint)texture.Width); height = Math.Min(height, (uint)texture.Height); layers = Math.Min(layers, (uint)texture.Layers); @@ -130,7 +120,6 @@ namespace Ryujinx.Graphics.Vulkan } AttachmentIntegerFormatMask = attachmentIntegerFormatMask; - LogicOpsAllowed = !allFormatsFloatOrSrgb; if (depthStencil is TextureView dsTexture && dsTexture.Valid) { @@ -250,7 +239,7 @@ namespace Ryujinx.Graphics.Vulkan Layers = Layers, }; - api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); + api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); return new Auto(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments); } @@ -286,44 +275,10 @@ namespace Ryujinx.Graphics.Vulkan _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true); - gd.Barriers.Flush(cbs, false, null, null); + gd.Barriers.Flush(cbs.CommandBuffer, false, null); } - public void AddStoreOpUsage() - { - if (_colors != null) - { - foreach (var color in _colors) - { - color.Storage?.AddStoreOpUsage(false); - } - } - - _depthStencil?.Storage?.AddStoreOpUsage(true); - } - - public void ClearBindings() - { - _depthStencil?.Storage.ClearBindings(); - - for (int i = 0; i < _colorsCanonical.Length; i++) - { - _colorsCanonical[i]?.Storage.ClearBindings(); - } - } - - public void AddBindings() - { - _depthStencil?.Storage.AddBinding(_depthStencil); - - for (int i = 0; i < _colorsCanonical.Length; i++) - { - TextureView color = _colorsCanonical[i]; - color?.Storage.AddBinding(color); - } - } - - public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( + public (Auto renderPass, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, CommandBufferScoped cbs) diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index bd17867b1..b6694bcb3 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -46,8 +46,6 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsViewportArray2; public readonly bool SupportsHostImportedMemory; public readonly bool SupportsDepthClipControl; - public readonly bool SupportsAttachmentFeedbackLoop; - public readonly bool SupportsDynamicAttachmentFeedbackLoop; public readonly uint SubgroupSize; public readonly SampleCountFlags SupportedSampleCounts; public readonly PortabilitySubsetFlags PortabilitySubset; @@ -86,8 +84,6 @@ namespace Ryujinx.Graphics.Vulkan bool supportsViewportArray2, bool supportsHostImportedMemory, bool supportsDepthClipControl, - bool supportsAttachmentFeedbackLoop, - bool supportsDynamicAttachmentFeedbackLoop, uint subgroupSize, SampleCountFlags supportedSampleCounts, PortabilitySubsetFlags portabilitySubset, @@ -125,8 +121,6 @@ namespace Ryujinx.Graphics.Vulkan SupportsViewportArray2 = supportsViewportArray2; SupportsHostImportedMemory = supportsHostImportedMemory; SupportsDepthClipControl = supportsDepthClipControl; - SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop; - SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop; SubgroupSize = subgroupSize; SupportedSampleCounts = supportedSampleCounts; PortabilitySubset = portabilitySubset; diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index b7c42aff0..3efb1119f 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan var strideChangeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] { @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan var colorCopyResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0) - .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] { @@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] { @@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] { @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] @@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); - _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(dstFormat)); + _pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat); int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; @@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan var dstView = Create2DLayerView(dst, dstLayer + z, 0); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); - _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format)); + _pipeline.SetImage(ShaderStage.Compute, 0, dstView, format); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs index ff1565246..baccc698f 100644 --- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = &importInfo, }; - Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory); + Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory); if (result < Result.Success) { diff --git a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs index 019286d28..38a5b6b48 100644 --- a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs +++ b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Vulkan { - class ImageArray : ResourceArray, IImageArray + class ImageArray : IImageArray { private readonly VulkanRenderer _gd; @@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Vulkan { public TextureStorage Storage; public TextureView View; + public GAL.Format ImageFormat; } private readonly TextureRef[] _textureRefs; @@ -28,6 +29,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly bool _isBuffer; + public bool Bound; + public ImageArray(VulkanRenderer gd, int size, bool isBuffer) { _gd = gd; @@ -51,6 +54,16 @@ namespace Ryujinx.Graphics.Vulkan _isBuffer = isBuffer; } + public void SetFormats(int index, GAL.Format[] imageFormats) + { + for (int i = 0; i < imageFormats.Length; i++) + { + _textureRefs[index + i].ImageFormat = imageFormats[i]; + } + + SetDirty(); + } + public void SetImages(int index, ITexture[] images) { for (int i = 0; i < images.Length; i++) @@ -84,7 +97,8 @@ namespace Ryujinx.Graphics.Vulkan { _cachedCommandBufferIndex = -1; _storages = null; - SetDirty(_gd, isImage: true); + + _gd.PipelineInternal.ForceImageDirty(); } public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags) @@ -131,7 +145,7 @@ namespace Ryujinx.Graphics.Vulkan ref var texture = ref textures[i]; ref var refs = ref _textureRefs[i]; - if (i > 0 && _textureRefs[i - 1].View == refs.View) + if (i > 0 && _textureRefs[i - 1].View == refs.View && _textureRefs[i - 1].ImageFormat == refs.ImageFormat) { texture = textures[i - 1]; @@ -139,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan } texture.ImageLayout = ImageLayout.General; - texture.ImageView = refs.View?.GetIdentityImageView().Get(cbs).Value ?? default; + texture.ImageView = refs.View?.GetView(refs.ImageFormat).GetIdentityImageView().Get(cbs).Value ?? default; if (texture.ImageView.Handle == 0) { @@ -156,52 +170,10 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < bufferTextures.Length; i++) { - bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, true) ?? default; + bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, _textureRefs[i].ImageFormat, true) ?? default; } return bufferTextures; } - - public DescriptorSet[] GetDescriptorSets( - Device device, - CommandBufferScoped cbs, - DescriptorSetTemplateUpdater templateUpdater, - ShaderCollection program, - int setIndex, - TextureView dummyTexture) - { - if (TryGetCachedDescriptorSets(cbs, program, setIndex, out DescriptorSet[] sets)) - { - // We still need to ensure the current command buffer holds a reference to all used textures. - - if (!_isBuffer) - { - GetImageInfos(_gd, cbs, dummyTexture); - } - else - { - GetBufferViews(cbs); - } - - return sets; - } - - DescriptorSetTemplate template = program.Templates[setIndex]; - - DescriptorSetTemplateWriter tu = templateUpdater.Begin(template); - - if (!_isBuffer) - { - tu.Push(GetImageInfos(_gd, cbs, dummyTexture)); - } - else - { - tu.Push(GetBufferViews(cbs)); - } - - templateUpdater.Commit(_gd, device, sets[0]); - - return sets; - } } } diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs index 3d42ed7e2..a1acc90f9 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan MemoryTypeIndex = (uint)MemoryTypeIndex, }; - _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); + _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); IntPtr hostPointer = IntPtr.Zero; diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index 930d6b525..457240aa0 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -1,4 +1,3 @@ -using Silk.NET.Core.Loader; using Silk.NET.Vulkan; using System; using System.Runtime.InteropServices; @@ -9,8 +8,6 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK [SupportedOSPlatform("macos")] public static partial class MVKInitialization { - private const string VulkanLib = "libvulkan.dylib"; - [LibraryImport("libMoltenVK.dylib")] private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize); @@ -32,20 +29,5 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); } - - private static string[] Resolver(string path) - { - if (path.EndsWith(VulkanLib)) - { - path = path[..^VulkanLib.Length] + "libMoltenVK.dylib"; - return [path]; - } - return Array.Empty(); - } - - public static void InitializeResolver() - { - ((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver); - } } } diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index b42524712..0bce3b72d 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Memory; using Silk.NET.Vulkan; using System; @@ -166,15 +165,14 @@ namespace Ryujinx.Graphics.Vulkan /// True if all fences were signaled before the timeout expired, false otherwise private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout) { - using SpanOwner fenceHoldersOwner = SpanOwner.Rent(CommandBufferPool.MaxCommandBuffers); - Span fenceHolders = fenceHoldersOwner.Span; + Span fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); Span fences = stackalloc Fence[count]; int fenceCount = 0; - for (int i = 0; i < fences.Length; i++) + for (int i = 0; i < count; i++) { if (fenceHolders[i].TryGet(out Fence fence)) { @@ -196,23 +194,18 @@ namespace Ryujinx.Graphics.Vulkan bool signaled = true; - try + if (hasTimeout) { - if (hasTimeout) - { - signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout); - } - else - { - FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]); - } + signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout); } - finally + else { - for (int i = 0; i < fenceCount; i++) - { - fenceHolders[i].PutLock(); - } + FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]); + } + + for (int i = 0; i < fenceCount; i++) + { + fenceHolders[i].Put(); } return signaled; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index addad83fd..41ab84d94 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; -using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -34,7 +33,6 @@ namespace Ryujinx.Graphics.Vulkan public readonly Action EndRenderPassDelegate; protected PipelineDynamicState DynamicState; - protected bool IsMainPipeline; private PipelineState _newState; private bool _graphicsStateDirty; private bool _computeStateDirty; @@ -57,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan protected FramebufferParams FramebufferParams; private Auto _framebuffer; - private RenderPassHolder _rpHolder; private Auto _renderPass; private RenderPassHolder _nullRenderPass; private int _writtenAttachmentCount; @@ -87,10 +84,9 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfEnabled; private bool _tfActive; - private FeedbackLoopAspects _feedbackLoop; - private bool _passWritesDepthStencil; - private readonly PipelineColorBlendAttachmentState[] _storedBlend; + + private ulong _drawCountSinceBarrier; public ulong DrawCount { get; private set; } public bool RenderPassActive { get; private set; } @@ -107,9 +103,9 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PipelineCacheCreateInfo, }; - gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); + gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); - _descriptorSetUpdater = new DescriptorSetUpdater(gd, device); + _descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this); _vertexBufferUpdater = new VertexBufferUpdater(gd); _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers]; @@ -131,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan public void Initialize() { - _descriptorSetUpdater.Initialize(IsMainPipeline); + _descriptorSetUpdater.Initialize(); QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false); TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true); @@ -139,7 +135,48 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Barrier() { - Gd.Barriers.QueueMemoryBarrier(); + if (_drawCountSinceBarrier != DrawCount) + { + _drawCountSinceBarrier = DrawCount; + + // Barriers are not supported inside a render pass on Apple GPUs. + // As a workaround, end the render pass. + if (Gd.Vendor == Vendor.Apple) + { + EndRenderPass(); + } + } + + MemoryBarrier memoryBarrier = new() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + }; + + PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit; + + if (Gd.Capabilities.SupportsGeometryShader) + { + pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit; + } + + if (Gd.Capabilities.SupportsTessellationShader) + { + pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit; + } + + Gd.Api.CmdPipelineBarrier( + CommandBuffer, + pipelineStageFlags, + pipelineStageFlags, + 0, + 1, + memoryBarrier, + 0, + null, + 0, + null); } public void ComputeBarrier() @@ -166,7 +203,6 @@ namespace Ryujinx.Graphics.Vulkan public void BeginTransformFeedback(PrimitiveTopology topology) { - Gd.Barriers.EnableTfbBarriers(true); _tfEnabled = true; } @@ -213,7 +249,7 @@ namespace Ryujinx.Graphics.Vulkan CreateRenderPass(); } - Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); BeginRenderPass(); @@ -251,7 +287,7 @@ namespace Ryujinx.Graphics.Vulkan CreateRenderPass(); } - Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); BeginRenderPass(); @@ -263,7 +299,24 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void CommandBufferBarrier() { - Gd.Barriers.QueueCommandBufferBarrier(); + MemoryBarrier memoryBarrier = new() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = BufferHolder.DefaultAccessFlags, + DstAccessMask = AccessFlags.IndirectCommandReadBit, + }; + + Gd.Api.CmdPipelineBarrier( + CommandBuffer, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.DrawIndirectBit, + 0, + 1, + memoryBarrier, + 0, + null, + 0, + null); } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -636,9 +689,9 @@ namespace Ryujinx.Graphics.Vulkan var oldStencilTestEnable = _newState.StencilTestEnable; var oldDepthTestEnable = _newState.DepthTestEnable; var oldDepthWriteEnable = _newState.DepthWriteEnable; + var oldTopology = _newState.Topology; var oldViewports = DynamicState.Viewports; var oldViewportsCount = _newState.ViewportsCount; - var oldTopology = _topology; _newState.CullMode = CullModeFlags.None; _newState.StencilTestEnable = false; @@ -658,7 +711,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.StencilTestEnable = oldStencilTestEnable; _newState.DepthTestEnable = oldDepthTestEnable; _newState.DepthWriteEnable = oldDepthWriteEnable; - SetPrimitiveTopology(oldTopology); + _newState.Topology = oldTopology; DynamicState.SetViewports(ref oldViewports, oldViewportsCount); @@ -669,7 +722,6 @@ namespace Ryujinx.Graphics.Vulkan public void EndTransformFeedback() { - Gd.Barriers.EnableTfbBarriers(false); PauseTransformFeedbackInternal(); _tfEnabled = false; } @@ -699,12 +751,14 @@ namespace Ryujinx.Graphics.Vulkan _vertexBufferUpdater.Commit(Cbs); } +#pragma warning disable CA1822 // Mark member as static public void SetAlphaTest(bool enable, float reference, CompareOp op) { // This is currently handled using shader specialization, as Vulkan does not support alpha test. // In the future, we may want to use this to write the reference value into the support buffer, // to avoid creating one version of the shader per reference value used. } +#pragma warning restore CA1822 public void SetBlendState(AdvancedBlendDescriptor blend) { @@ -819,8 +873,6 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthTestEnable = depthTest.TestEnable; _newState.DepthWriteEnable = depthTest.WriteEnable; _newState.DepthCompareOp = depthTest.Func.Convert(); - - UpdatePassDepthStencil(); SignalStateChange(); } @@ -836,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetImage(ShaderStage stage, int binding, ITexture image) + public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat) { - _descriptorSetUpdater.SetImage(Cbs, stage, binding, image); + _descriptorSetUpdater.SetImage(Cbs, stage, binding, image, imageFormat); } public void SetImage(int binding, Auto image) @@ -851,11 +903,6 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetImageArray(Cbs, stage, binding, array); } - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) - { - _descriptorSetUpdater.SetImageArraySeparate(Cbs, stage, setIndex, array); - } - public void SetIndexBuffer(BufferRange buffer, IndexType type) { if (buffer.Handle != BufferHandle.Null) @@ -898,6 +945,7 @@ namespace Ryujinx.Graphics.Vulkan // TODO: Default levels (likely needs emulation on shaders?) } +#pragma warning disable CA1822 // Mark member as static public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) { // TODO. @@ -907,6 +955,7 @@ namespace Ryujinx.Graphics.Vulkan { // TODO. } +#pragma warning restore CA1822 public void SetPrimitiveRestart(bool enable, int index) { @@ -970,13 +1019,6 @@ namespace Ryujinx.Graphics.Vulkan { _newState.RasterizerDiscardEnable = discard; SignalStateChange(); - - if (!discard && Gd.IsQualcommProprietary) - { - // On Adreno, enabling rasterizer discard somehow corrupts the viewport state. - // Force it to be updated on next use to work around this bug. - DynamicState.ForceAllDirty(); - } } public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) @@ -1086,8 +1128,6 @@ namespace Ryujinx.Graphics.Vulkan _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert(); _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert(); _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); - - UpdatePassDepthStencil(); SignalStateChange(); } @@ -1116,11 +1156,6 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetTextureArray(Cbs, stage, binding, array); } - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) - { - _descriptorSetUpdater.SetTextureArraySeparate(Cbs, stage, setIndex, array); - } - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { PauseTransformFeedbackInternal(); @@ -1151,10 +1186,12 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); } +#pragma warning disable CA1822 // Mark member as static public void SetUserClipDistance(int index, bool enableClip) { // TODO. } +#pragma warning restore CA1822 public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { @@ -1360,7 +1397,24 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void TextureBarrier() { - Gd.Barriers.QueueTextureBarrier(); + MemoryBarrier memoryBarrier = new() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + }; + + Gd.Api.CmdPipelineBarrier( + CommandBuffer, + PipelineStageFlags.FragmentShaderBit, + PipelineStageFlags.FragmentShaderBit, + 0, + 1, + memoryBarrier, + 0, + null, + 0, + null); } public void TextureBarrierTiled() @@ -1435,23 +1489,7 @@ namespace Ryujinx.Graphics.Vulkan } } - if (IsMainPipeline) - { - FramebufferParams?.ClearBindings(); - } - FramebufferParams = new FramebufferParams(Device, colors, depthStencil); - - if (IsMainPipeline) - { - FramebufferParams.AddBindings(); - - _newState.FeedbackLoopAspects = FeedbackLoopAspects.None; - _bindingBarriersDirty = true; - } - - _passWritesDepthStencil = false; - UpdatePassDepthStencil(); UpdatePipelineAttachmentFormats(); } @@ -1460,7 +1498,6 @@ namespace Ryujinx.Graphics.Vulkan var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); _newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask; - _newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed; for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { @@ -1483,15 +1520,12 @@ namespace Ryujinx.Graphics.Vulkan // Use the null framebuffer. _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams); - _rpHolder = _nullRenderPass; _renderPass = _nullRenderPass.GetRenderPass(); _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams); } else { - (_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); - - _renderPass = _rpHolder.GetRenderPass(); + (_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); } } @@ -1518,82 +1552,11 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); } - private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects) - { - if (_feedbackLoop != aspects) - { - if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) - { - DynamicState.SetFeedbackLoop(aspects); - } - else - { - _newState.FeedbackLoopAspects = aspects; - } - - _feedbackLoop = aspects; - - return true; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool UpdateFeedbackLoop() - { - List hazards = _descriptorSetUpdater.FeedbackLoopHazards; - - if ((hazards?.Count ?? 0) > 0) - { - FeedbackLoopAspects aspects = 0; - - foreach (TextureView view in hazards) - { - // May need to enforce feedback loop layout here in the future. - // Though technically, it should always work with the general layout. - - if (view.Info.Format.IsDepthOrStencil()) - { - if (_passWritesDepthStencil) - { - // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop. - - aspects |= FeedbackLoopAspects.Depth; - } - } - else - { - aspects |= FeedbackLoopAspects.Color; - } - } - - return ChangeFeedbackLoop(aspects); - } - else if (_feedbackLoop != 0) - { - return ChangeFeedbackLoop(FeedbackLoopAspects.None); - } - - return false; - } - - private void UpdatePassDepthStencil() - { - if (!RenderPassActive) - { - _passWritesDepthStencil = false; - } - - // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check. - _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable; - } - private bool RecreateGraphicsPipelineIfNeeded() { if (AutoFlush.ShouldFlushDraw(DrawCount)) @@ -1601,7 +1564,7 @@ namespace Ryujinx.Graphics.Vulkan Gd.FlushAllCommands(); } - DynamicState.ReplayIfDirty(Gd, CommandBuffer); + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); if (_needsIndexBufferRebind && _indexBufferPattern == null) { @@ -1635,15 +1598,7 @@ namespace Ryujinx.Graphics.Vulkan _vertexBufferUpdater.Commit(Cbs); } - if (_bindingBarriersDirty) - { - // Stale barriers may have been activated by switching program. Emit any that are relevant. - _descriptorSetUpdater.InsertBindingBarriers(Cbs); - - _bindingBarriersDirty = false; - } - - if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) + if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) { if (!CreatePipeline(PipelineBindPoint.Graphics)) { @@ -1652,9 +1607,17 @@ namespace Ryujinx.Graphics.Vulkan _graphicsStateDirty = false; Pbp = PipelineBindPoint.Graphics; + + if (_bindingBarriersDirty) + { + // Stale barriers may have been activated by switching program. Emit any that are relevant. + _descriptorSetUpdater.InsertBindingBarriers(Cbs); + + _bindingBarriersDirty = false; + } } - Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); @@ -1724,7 +1687,7 @@ namespace Ryujinx.Graphics.Vulkan ClearValueCount = 1, }; - Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline); + Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); RenderPassActive = true; } } @@ -1733,8 +1696,6 @@ namespace Ryujinx.Graphics.Vulkan { if (RenderPassActive) { - FramebufferParams.AddStoreOpUsage(); - PauseTransformFeedbackInternal(); Gd.Api.CmdEndRenderPass(CommandBuffer); SignalRenderPassEnd(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 85069c6b2..95b480a5e 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -9,6 +9,13 @@ namespace Ryujinx.Graphics.Vulkan { static class PipelineConverter { + private const AccessFlags SubpassAccessMask = + AccessFlags.MemoryReadBit | + AccessFlags.MemoryWriteBit | + AccessFlags.ShaderReadBit | + AccessFlags.ColorAttachmentWriteBit | + AccessFlags.DepthStencilAttachmentWriteBit; + public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) { const int MaxAttachments = Constants.MaxRenderTargets + 1; @@ -101,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = CreateSubpassDependency(gd); + var subpassDependency = CreateSubpassDependency(); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { @@ -116,39 +123,35 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); return new DisposableRenderPass(gd.Api, device, renderPass); } } - public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd) + public static SubpassDependency CreateSubpassDependency() { - var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); - return new SubpassDependency( 0, 0, - stages, - stages, - access, - access, + PipelineStageFlags.AllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, + SubpassAccessMask, + SubpassAccessMask, 0); } - public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd) + public unsafe static SubpassDependency2 CreateSubpassDependency2() { - var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); - return new SubpassDependency2( StructureType.SubpassDependency2, null, 0, 0, - stages, - stages, - access, - access, + PipelineStageFlags.AllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, + SubpassAccessMask, + SubpassAccessMask, 0); } @@ -177,6 +180,9 @@ namespace Ryujinx.Graphics.Vulkan pipeline.LogicOpEnable = state.LogicOpEnable; pipeline.LogicOp = state.LogicOp.Convert(); + pipeline.MinDepthBounds = 0f; // Not implemented. + pipeline.MaxDepthBounds = 0f; // Not implemented. + pipeline.PatchControlPoints = state.PatchControlPoints; pipeline.PolygonMode = PolygonMode.Fill; // Not implemented. pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable; @@ -202,11 +208,17 @@ namespace Ryujinx.Graphics.Vulkan pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert(); pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert(); pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert(); + pipeline.StencilFrontCompareMask = 0; + pipeline.StencilFrontWriteMask = 0; + pipeline.StencilFrontReference = 0; pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert(); pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert(); pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert(); pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert(); + pipeline.StencilBackCompareMask = 0; + pipeline.StencilBackWriteMask = 0; + pipeline.StencilBackReference = 0; pipeline.StencilTestEnable = state.StencilTest.TestEnable; @@ -290,7 +302,6 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int maxColorAttachmentIndex = -1; uint attachmentIntegerFormatMask = 0; - bool allFormatsFloatOrSrgb = true; for (int i = 0; i < Constants.MaxRenderTargets; i++) { @@ -303,8 +314,6 @@ namespace Ryujinx.Graphics.Vulkan { attachmentIntegerFormatMask |= 1u << i; } - - allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb(); } } @@ -316,7 +325,6 @@ namespace Ryujinx.Graphics.Vulkan pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask; - pipeline.Internal.LogicOpsAllowed = attachmentCount == 0 || !allFormatsFloatOrSrgb; return pipeline; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index ad26ff7b3..1cc33f728 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Memory; using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.EXT; namespace Ryujinx.Graphics.Vulkan { @@ -22,8 +21,6 @@ namespace Ryujinx.Graphics.Vulkan private Array4 _blendConstants; - private FeedbackLoopAspects _feedbackLoopAspects; - public uint ViewportsCount; public Array16 Viewports; @@ -35,8 +32,7 @@ namespace Ryujinx.Graphics.Vulkan Scissor = 1 << 2, Stencil = 1 << 3, Viewport = 1 << 4, - FeedbackLoop = 1 << 5, - All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop, + All = Blend | DepthBias | Scissor | Stencil | Viewport, } private DirtyFlags _dirty; @@ -103,22 +99,13 @@ namespace Ryujinx.Graphics.Vulkan } } - public void SetFeedbackLoop(FeedbackLoopAspects aspects) - { - _feedbackLoopAspects = aspects; - - _dirty |= DirtyFlags.FeedbackLoop; - } - public void ForceAllDirty() { _dirty = DirtyFlags.All; } - public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) + public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer) { - Vk api = gd.Api; - if (_dirty.HasFlag(DirtyFlags.Blend)) { RecordBlend(api, commandBuffer); @@ -144,11 +131,6 @@ namespace Ryujinx.Graphics.Vulkan RecordViewport(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) - { - RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer); - } - _dirty = DirtyFlags.None; } @@ -187,17 +169,5 @@ namespace Ryujinx.Graphics.Vulkan api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan()); } } - - private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer) - { - ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0; - - if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0) - { - aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit; - } - - api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects); - } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 54d43bdba..4987548cd 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Vulkan _activeBufferMirrors = new(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; - - IsMainPipeline = true; } private void CopyPendingQuery() @@ -49,11 +47,10 @@ namespace Ryujinx.Graphics.Vulkan return; } - if (componentMask != 0xf || Gd.IsQualcommProprietary) + if (componentMask != 0xf) { // We can't use CmdClearAttachments if not writing all components, // because on Vulkan, the pipeline state does not affect clears. - // On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all. var dstTexture = FramebufferParams.GetColorView(index); if (dstTexture == null) { @@ -90,11 +87,10 @@ namespace Ryujinx.Graphics.Vulkan return; } - if ((stencilMask != 0 && stencilMask != 0xff) || Gd.IsQualcommProprietary) + if (stencilMask != 0 && stencilMask != 0xff) { // We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits, // because on Vulkan, the pipeline state does not affect clears. - // On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all. var dstTexture = FramebufferParams.GetDepthStencilView(); if (dstTexture == null) { @@ -226,6 +222,20 @@ namespace Ryujinx.Graphics.Vulkan } } + private void TryBackingSwaps() + { + CommandBufferScoped? cbs = null; + + _backingSwaps.RemoveAll(holder => holder.TryBackingSwap(ref cbs)); + + cbs?.Dispose(); + } + + public void AddBackingSwap(BufferHolder holder) + { + _backingSwaps.Add(holder); + } + public void Restore() { if (Pipeline != null) @@ -237,7 +247,7 @@ namespace Ryujinx.Graphics.Vulkan if (Pipeline != null && Pbp == PipelineBindPoint.Graphics) { - DynamicState.ReplayIfDirty(Gd, CommandBuffer); + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); } } @@ -259,7 +269,7 @@ namespace Ryujinx.Graphics.Vulkan PreloadCbs = null; } - Gd.Barriers.Flush(Cbs, false, null, null); + Gd.Barriers.Flush(Cbs.CommandBuffer, false, null); CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; Gd.RegisterFlush(); @@ -281,6 +291,8 @@ namespace Ryujinx.Graphics.Vulkan Gd.ResetCounterPool(); + TryBackingSwaps(); + Restore(); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs index ae296b033..fb1f0a5ff 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs @@ -3,8 +3,6 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Vulkan { @@ -16,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; public DescriptorSetLayout[] DescriptorSetLayouts { get; } - public bool[] DescriptorSetLayoutsUpdateAfterBind { get; } public PipelineLayout PipelineLayout { get; } private readonly int[] _consumedDescriptorsPerSet; @@ -30,41 +27,6 @@ namespace Ryujinx.Graphics.Vulkan private int _dsLastCbIndex; private int _dsLastSubmissionCount; - private struct ManualDescriptorSetEntry - { - public Auto DescriptorSet; - public uint CbRefMask; - public bool InUse; - - public ManualDescriptorSetEntry(Auto descriptorSet, int cbIndex) - { - DescriptorSet = descriptorSet; - CbRefMask = 1u << cbIndex; - InUse = true; - } - } - - private readonly struct PendingManualDsConsumption - { - public FenceHolder Fence { get; } - public int CommandBufferIndex { get; } - public int SetIndex { get; } - public int CacheIndex { get; } - - public PendingManualDsConsumption(FenceHolder fence, int commandBufferIndex, int setIndex, int cacheIndex) - { - Fence = fence; - CommandBufferIndex = commandBufferIndex; - SetIndex = setIndex; - CacheIndex = cacheIndex; - fence.Get(); - } - } - - private readonly List[] _manualDsCache; - private readonly Queue _pendingManualDsConsumptions; - private readonly Queue[] _freeManualDsCacheEntries; - private readonly Dictionary _pdTemplates; private readonly ResourceDescriptorCollection _pdDescriptors; private long _lastPdUsage; @@ -88,9 +50,6 @@ namespace Ryujinx.Graphics.Vulkan } _dsCacheCursor = new int[setsCount]; - _manualDsCache = new List[setsCount]; - _pendingManualDsConsumptions = new Queue(); - _freeManualDsCacheEntries = new Queue[setsCount]; } public PipelineLayoutCacheEntry( @@ -99,11 +58,7 @@ namespace Ryujinx.Graphics.Vulkan ReadOnlyCollection setDescriptors, bool usePushDescriptors) : this(gd, device, setDescriptors.Count) { - ResourceLayouts layouts = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors); - - DescriptorSetLayouts = layouts.DescriptorSetLayouts; - DescriptorSetLayoutsUpdateAfterBind = layouts.DescriptorSetLayoutsUpdateAfterBind; - PipelineLayout = layouts.PipelineLayout; + (DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors); _consumedDescriptorsPerSet = new int[setDescriptors.Count]; _poolSizes = new DescriptorPoolSize[setDescriptors.Count][]; @@ -158,7 +113,7 @@ namespace Ryujinx.Graphics.Vulkan _poolSizes[setIndex], setIndex, _consumedDescriptorsPerSet[setIndex], - DescriptorSetLayoutsUpdateAfterBind[setIndex]); + false); list.Add(dsc); isNew = true; @@ -169,101 +124,6 @@ namespace Ryujinx.Graphics.Vulkan return list[index]; } - public Auto GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex) - { - FreeCompletedManualDescriptorSets(); - - var list = _manualDsCache[setIndex] ??= new(); - var span = CollectionsMarshal.AsSpan(list); - - Queue freeQueue = _freeManualDsCacheEntries[setIndex]; - - // Do we have at least one freed descriptor set? If so, just use that. - if (freeQueue != null && freeQueue.TryDequeue(out int freeIndex)) - { - ref ManualDescriptorSetEntry entry = ref span[freeIndex]; - - Debug.Assert(!entry.InUse && entry.CbRefMask == 0); - - entry.InUse = true; - entry.CbRefMask = 1u << cbs.CommandBufferIndex; - cacheIndex = freeIndex; - - _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, freeIndex)); - - return entry.DescriptorSet; - } - - // Otherwise create a new descriptor set, and add to our pending queue for command buffer consumption tracking. - var dsc = _descriptorSetManager.AllocateDescriptorSet( - _gd.Api, - DescriptorSetLayouts[setIndex], - _poolSizes[setIndex], - setIndex, - _consumedDescriptorsPerSet[setIndex], - DescriptorSetLayoutsUpdateAfterBind[setIndex]); - - cacheIndex = list.Count; - list.Add(new ManualDescriptorSetEntry(dsc, cbs.CommandBufferIndex)); - _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex)); - - return dsc; - } - - public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex) - { - FreeCompletedManualDescriptorSets(); - - var list = _manualDsCache[setIndex]; - var span = CollectionsMarshal.AsSpan(list); - ref var entry = ref span[cacheIndex]; - - uint cbMask = 1u << cbs.CommandBufferIndex; - - if ((entry.CbRefMask & cbMask) == 0) - { - entry.CbRefMask |= cbMask; - - _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex)); - } - } - - private void FreeCompletedManualDescriptorSets() - { - FenceHolder signalledFence = null; - while (_pendingManualDsConsumptions.TryPeek(out var pds) && (pds.Fence == signalledFence || pds.Fence.IsSignaled())) - { - signalledFence = pds.Fence; // Already checked - don't need to do it again. - var dequeued = _pendingManualDsConsumptions.Dequeue(); - Debug.Assert(dequeued.Fence == pds.Fence); - pds.Fence.Put(); - - var span = CollectionsMarshal.AsSpan(_manualDsCache[dequeued.SetIndex]); - ref var entry = ref span[dequeued.CacheIndex]; - entry.CbRefMask &= ~(1u << dequeued.CommandBufferIndex); - - if (!entry.InUse && entry.CbRefMask == 0) - { - // If not in use by any array, and not bound to any command buffer, the descriptor set can be re-used immediately. - (_freeManualDsCacheEntries[dequeued.SetIndex] ??= new()).Enqueue(dequeued.CacheIndex); - } - } - } - - public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex) - { - var list = _manualDsCache[setIndex]; - var span = CollectionsMarshal.AsSpan(list); - - span[cacheIndex].InUse = false; - - if (span[cacheIndex].CbRefMask == 0) - { - // This is no longer in use by any array, so if not bound to any command buffer, the descriptor set can be re-used immediately. - (_freeManualDsCacheEntries[setIndex] ??= new()).Enqueue(cacheIndex); - } - } - private static Span GetDescriptorPoolSizes(Span output, ResourceDescriptorCollection setDescriptor, uint multiplier) { int count = 0; @@ -344,21 +204,6 @@ namespace Ryujinx.Graphics.Vulkan } } - for (int i = 0; i < _manualDsCache.Length; i++) - { - if (_manualDsCache[i] == null) - { - continue; - } - - for (int j = 0; j < _manualDsCache[i].Count; j++) - { - _manualDsCache[i][j].DescriptorSet.Dispose(); - } - - _manualDsCache[i].Clear(); - } - _gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); for (int i = 0; i < DescriptorSetLayouts.Length; i++) @@ -366,11 +211,6 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null); } - while (_pendingManualDsConsumptions.TryDequeue(out var pds)) - { - pds.Fence.Put(); - } - _descriptorSetManager.Dispose(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index 8d7815616..8bf286c65 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -1,23 +1,18 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; -using System; using System.Collections.ObjectModel; namespace Ryujinx.Graphics.Vulkan { - record struct ResourceLayouts(DescriptorSetLayout[] DescriptorSetLayouts, bool[] DescriptorSetLayoutsUpdateAfterBind, PipelineLayout PipelineLayout); - static class PipelineLayoutFactory { - public static unsafe ResourceLayouts Create( + public static unsafe (DescriptorSetLayout[], PipelineLayout) Create( VulkanRenderer gd, Device device, ReadOnlyCollection setDescriptors, bool usePushDescriptors) { DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count]; - bool[] updateAfterBindFlags = new bool[setDescriptors.Count]; bool isMoltenVk = gd.IsMoltenVk; @@ -37,11 +32,10 @@ namespace Ryujinx.Graphics.Vulkan DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count]; - bool hasArray = false; - for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++) { ResourceDescriptor descriptor = rdc.Descriptors[descIndex]; + ResourceStages stages = descriptor.Stages; if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk) @@ -58,40 +52,19 @@ namespace Ryujinx.Graphics.Vulkan DescriptorCount = (uint)descriptor.Count, StageFlags = stages.Convert(), }; - - if (descriptor.Count > 1) - { - hasArray = true; - } } fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings) { - DescriptorSetLayoutCreateFlags flags = DescriptorSetLayoutCreateFlags.None; - - if (usePushDescriptors && setIndex == 0) - { - flags = DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr; - } - - if (gd.Vendor == Vendor.Intel && hasArray) - { - // Some vendors (like Intel) have low per-stage limits. - // We must set the flag if we exceed those limits. - flags |= DescriptorSetLayoutCreateFlags.UpdateAfterBindPoolBit; - - updateAfterBindFlags[setIndex] = true; - } - var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo { SType = StructureType.DescriptorSetLayoutCreateInfo, PBindings = pLayoutBindings, BindingCount = (uint)layoutBindings.Length, - Flags = flags, + Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None, }; - gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); + gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); } } @@ -109,7 +82,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); } - return new ResourceLayouts(layouts, updateAfterBindFlags, layout); + return (layouts, layout); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index a726b9edb..49c12b376 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -8,7 +8,6 @@ namespace Ryujinx.Graphics.Vulkan struct PipelineState : IDisposable { private const int RequiredSubgroupSize = 32; - private const int MaxDynamicStatesCount = 9; public PipelineUid Internal; @@ -72,238 +71,244 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32); } + public float MinDepthBounds + { + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 0) & 0xFFFFFFFF)); + set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); + } + + public float MaxDepthBounds + { + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 32) & 0xFFFFFFFF)); + set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); + } + public PolygonMode PolygonMode { - readonly get => (PolygonMode)((Internal.Id5 >> 0) & 0x3FFFFFFF); - set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); + readonly get => (PolygonMode)((Internal.Id6 >> 0) & 0x3FFFFFFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); } public uint StagesCount { - readonly get => (byte)((Internal.Id5 >> 30) & 0xFF); - set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); + readonly get => (byte)((Internal.Id6 >> 30) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); } public uint VertexAttributeDescriptionsCount { - readonly get => (byte)((Internal.Id5 >> 38) & 0xFF); - set => Internal.Id5 = (Internal.Id5 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); + readonly get => (byte)((Internal.Id6 >> 38) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); } public uint VertexBindingDescriptionsCount { - readonly get => (byte)((Internal.Id5 >> 46) & 0xFF); - set => Internal.Id5 = (Internal.Id5 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); + readonly get => (byte)((Internal.Id6 >> 46) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); } public uint ViewportsCount { - readonly get => (byte)((Internal.Id5 >> 54) & 0xFF); - set => Internal.Id5 = (Internal.Id5 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54); + readonly get => (byte)((Internal.Id6 >> 54) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54); } public uint ScissorsCount { - readonly get => (byte)((Internal.Id6 >> 0) & 0xFF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); + readonly get => (byte)((Internal.Id7 >> 0) & 0xFF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); } public uint ColorBlendAttachmentStateCount { - readonly get => (byte)((Internal.Id6 >> 8) & 0xFF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); + readonly get => (byte)((Internal.Id7 >> 8) & 0xFF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); } public PrimitiveTopology Topology { - readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); + readonly get => (PrimitiveTopology)((Internal.Id7 >> 16) & 0xF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); } public LogicOp LogicOp { - readonly get => (LogicOp)((Internal.Id6 >> 20) & 0xF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20); + readonly get => (LogicOp)((Internal.Id7 >> 20) & 0xF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20); } public CompareOp DepthCompareOp { - readonly get => (CompareOp)((Internal.Id6 >> 24) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24); + readonly get => (CompareOp)((Internal.Id7 >> 24) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24); } public StencilOp StencilFrontFailOp { - readonly get => (StencilOp)((Internal.Id6 >> 27) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27); + readonly get => (StencilOp)((Internal.Id7 >> 27) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27); } public StencilOp StencilFrontPassOp { - readonly get => (StencilOp)((Internal.Id6 >> 30) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30); + readonly get => (StencilOp)((Internal.Id7 >> 30) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30); } public StencilOp StencilFrontDepthFailOp { - readonly get => (StencilOp)((Internal.Id6 >> 33) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33); + readonly get => (StencilOp)((Internal.Id7 >> 33) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33); } public CompareOp StencilFrontCompareOp { - readonly get => (CompareOp)((Internal.Id6 >> 36) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36); + readonly get => (CompareOp)((Internal.Id7 >> 36) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36); } public StencilOp StencilBackFailOp { - readonly get => (StencilOp)((Internal.Id6 >> 39) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39); + readonly get => (StencilOp)((Internal.Id7 >> 39) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39); } public StencilOp StencilBackPassOp { - readonly get => (StencilOp)((Internal.Id6 >> 42) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42); + readonly get => (StencilOp)((Internal.Id7 >> 42) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42); } public StencilOp StencilBackDepthFailOp { - readonly get => (StencilOp)((Internal.Id6 >> 45) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45); + readonly get => (StencilOp)((Internal.Id7 >> 45) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45); } public CompareOp StencilBackCompareOp { - readonly get => (CompareOp)((Internal.Id6 >> 48) & 0x7); - set => Internal.Id6 = (Internal.Id6 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48); + readonly get => (CompareOp)((Internal.Id7 >> 48) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48); } public CullModeFlags CullMode { - readonly get => (CullModeFlags)((Internal.Id6 >> 51) & 0x3); - set => Internal.Id6 = (Internal.Id6 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51); + readonly get => (CullModeFlags)((Internal.Id7 >> 51) & 0x3); + set => Internal.Id7 = (Internal.Id7 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51); } public bool PrimitiveRestartEnable { - readonly get => ((Internal.Id6 >> 53) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53); + readonly get => ((Internal.Id7 >> 53) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53); } public bool DepthClampEnable { - readonly get => ((Internal.Id6 >> 54) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54); + readonly get => ((Internal.Id7 >> 54) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54); } public bool RasterizerDiscardEnable { - readonly get => ((Internal.Id6 >> 55) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55); + readonly get => ((Internal.Id7 >> 55) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55); } public FrontFace FrontFace { - readonly get => (FrontFace)((Internal.Id6 >> 56) & 0x1); - set => Internal.Id6 = (Internal.Id6 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56); + readonly get => (FrontFace)((Internal.Id7 >> 56) & 0x1); + set => Internal.Id7 = (Internal.Id7 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56); } public bool DepthBiasEnable { - readonly get => ((Internal.Id6 >> 57) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57); + readonly get => ((Internal.Id7 >> 57) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57); } public bool DepthTestEnable { - readonly get => ((Internal.Id6 >> 58) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58); + readonly get => ((Internal.Id7 >> 58) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58); } public bool DepthWriteEnable { - readonly get => ((Internal.Id6 >> 59) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59); + readonly get => ((Internal.Id7 >> 59) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59); } public bool DepthBoundsTestEnable { - readonly get => ((Internal.Id6 >> 60) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60); + readonly get => ((Internal.Id7 >> 60) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60); } public bool StencilTestEnable { - readonly get => ((Internal.Id6 >> 61) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61); + readonly get => ((Internal.Id7 >> 61) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61); } public bool LogicOpEnable { - readonly get => ((Internal.Id6 >> 62) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62); + readonly get => ((Internal.Id7 >> 62) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62); } public bool HasDepthStencil { - readonly get => ((Internal.Id6 >> 63) & 0x1) != 0UL; - set => Internal.Id6 = (Internal.Id6 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); + readonly get => ((Internal.Id7 >> 63) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); } public uint PatchControlPoints { - readonly get => (uint)((Internal.Id7 >> 0) & 0xFFFFFFFF); - set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)value << 0); + readonly get => (uint)((Internal.Id8 >> 0) & 0xFFFFFFFF); + set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint SamplesCount { - readonly get => (uint)((Internal.Id7 >> 32) & 0xFFFFFFFF); - set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)value << 32); + readonly get => (uint)((Internal.Id8 >> 32) & 0xFFFFFFFF); + set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF) | ((ulong)value << 32); } public bool AlphaToCoverageEnable { - readonly get => ((Internal.Id8 >> 0) & 0x1) != 0UL; - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); + readonly get => ((Internal.Id9 >> 0) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); } public bool AlphaToOneEnable { - readonly get => ((Internal.Id8 >> 1) & 0x1) != 0UL; - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); + readonly get => ((Internal.Id9 >> 1) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); } public bool AdvancedBlendSrcPreMultiplied { - readonly get => ((Internal.Id8 >> 2) & 0x1) != 0UL; - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); + readonly get => ((Internal.Id9 >> 2) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); } public bool AdvancedBlendDstPreMultiplied { - readonly get => ((Internal.Id8 >> 3) & 0x1) != 0UL; - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3); + readonly get => ((Internal.Id9 >> 3) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3); } public BlendOverlapEXT AdvancedBlendOverlap { - readonly get => (BlendOverlapEXT)((Internal.Id8 >> 4) & 0x3); - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); + readonly get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3); + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); } public bool DepthMode { - readonly get => ((Internal.Id8 >> 6) & 0x1) != 0UL; - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); - } - - public FeedbackLoopAspects FeedbackLoopAspects - { - readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3); - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7); + readonly get => ((Internal.Id9 >> 6) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); } public bool HasTessellationControlShader; @@ -403,6 +408,8 @@ namespace Ryujinx.Graphics.Vulkan fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0]) fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0]) + fixed (Viewport* pViewports = &Internal.Viewports[0]) + fixed (Rect2D* pScissors = &Internal.Scissors[0]) fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0]) { var vertexInputState = new PipelineVertexInputStateCreateInfo @@ -446,7 +453,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PipelineInputAssemblyStateCreateInfo, PrimitiveRestartEnable = primitiveRestartEnable, - Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology, + Topology = Topology, }; var tessellationState = new PipelineTessellationStateCreateInfo @@ -465,13 +472,18 @@ namespace Ryujinx.Graphics.Vulkan CullMode = CullMode, FrontFace = FrontFace, DepthBiasEnable = DepthBiasEnable, + DepthBiasClamp = DepthBiasClamp, + DepthBiasConstantFactor = DepthBiasConstantFactor, + DepthBiasSlopeFactor = DepthBiasSlopeFactor, }; var viewportState = new PipelineViewportStateCreateInfo { SType = StructureType.PipelineViewportStateCreateInfo, ViewportCount = ViewportsCount, + PViewports = pViewports, ScissorCount = ScissorsCount, + PScissors = pScissors, }; if (gd.Capabilities.SupportsDepthClipControl) @@ -499,13 +511,19 @@ namespace Ryujinx.Graphics.Vulkan StencilFrontFailOp, StencilFrontPassOp, StencilFrontDepthFailOp, - StencilFrontCompareOp); + StencilFrontCompareOp, + StencilFrontCompareMask, + StencilFrontWriteMask, + StencilFrontReference); var stencilBack = new StencilOpState( StencilBackFailOp, StencilBackPassOp, StencilBackDepthFailOp, - StencilBackCompareOp); + StencilBackCompareOp, + StencilBackCompareMask, + StencilBackWriteMask, + StencilBackReference); var depthStencilState = new PipelineDepthStencilStateCreateInfo { @@ -513,10 +531,12 @@ namespace Ryujinx.Graphics.Vulkan DepthTestEnable = DepthTestEnable, DepthWriteEnable = DepthWriteEnable, DepthCompareOp = DepthCompareOp, - DepthBoundsTestEnable = false, + DepthBoundsTestEnable = DepthBoundsTestEnable, StencilTestEnable = StencilTestEnable, Front = stencilFront, Back = stencilBack, + MinDepthBounds = MinDepthBounds, + MaxDepthBounds = MaxDepthBounds, }; uint blendEnables = 0; @@ -540,14 +560,10 @@ namespace Ryujinx.Graphics.Vulkan } } - // Vendors other than NVIDIA have a bug where it enables logical operations even for float formats, - // so we need to force disable them here. - bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed); - var colorBlendState = new PipelineColorBlendStateCreateInfo { SType = StructureType.PipelineColorBlendStateCreateInfo, - LogicOpEnable = logicOpEnable, + LogicOpEnable = LogicOpEnable, LogicOp = LogicOp, AttachmentCount = ColorBlendAttachmentStateCount, PAttachments = pColorBlendAttachmentState, @@ -571,28 +587,22 @@ namespace Ryujinx.Graphics.Vulkan } bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; - bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop; + int dynamicStatesCount = supportsExtDynamicState ? 9 : 8; - DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount]; - - int dynamicStatesCount = 7; + DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount]; dynamicStates[0] = DynamicState.Viewport; dynamicStates[1] = DynamicState.Scissor; dynamicStates[2] = DynamicState.DepthBias; - dynamicStates[3] = DynamicState.StencilCompareMask; - dynamicStates[4] = DynamicState.StencilWriteMask; - dynamicStates[5] = DynamicState.StencilReference; - dynamicStates[6] = DynamicState.BlendConstants; + dynamicStates[3] = DynamicState.DepthBounds; + dynamicStates[4] = DynamicState.StencilCompareMask; + dynamicStates[5] = DynamicState.StencilWriteMask; + dynamicStates[6] = DynamicState.StencilReference; + dynamicStates[7] = DynamicState.BlendConstants; if (supportsExtDynamicState) { - dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt; - } - - if (supportsFeedbackLoopDynamicState) - { - dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt; + dynamicStates[8] = DynamicState.VertexInputBindingStrideExt; } var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo @@ -602,27 +612,9 @@ namespace Ryujinx.Graphics.Vulkan PDynamicStates = dynamicStates, }; - PipelineCreateFlags flags = 0; - - if (gd.Capabilities.SupportsAttachmentFeedbackLoop) - { - FeedbackLoopAspects aspects = FeedbackLoopAspects; - - if ((aspects & FeedbackLoopAspects.Color) != 0) - { - flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt; - } - - if ((aspects & FeedbackLoopAspects.Depth) != 0) - { - flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt; - } - } - var pipelineCreateInfo = new GraphicsPipelineCreateInfo { SType = StructureType.GraphicsPipelineCreateInfo, - Flags = flags, StageCount = StagesCount, PStages = Stages.Pointer, PVertexInputState = &vertexInputState, @@ -636,6 +628,7 @@ namespace Ryujinx.Graphics.Vulkan PDynamicState = &pipelineDynamicStateCreateInfo, Layout = PipelineLayout, RenderPass = renderPass, + BasePipelineIndex = -1, }; Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index c56224216..3448d9743 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -17,21 +17,23 @@ namespace Ryujinx.Graphics.Vulkan public ulong Id4; public ulong Id5; public ulong Id6; - public ulong Id7; - public ulong Id8; - private readonly uint VertexAttributeDescriptionsCount => (byte)((Id5 >> 38) & 0xFF); - private readonly uint VertexBindingDescriptionsCount => (byte)((Id5 >> 46) & 0xFF); - private readonly uint ColorBlendAttachmentStateCount => (byte)((Id6 >> 8) & 0xFF); - private readonly bool HasDepthStencil => ((Id6 >> 63) & 0x1) != 0UL; + public ulong Id8; + public ulong Id9; + + private readonly uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF); + private readonly uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF); + private readonly uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF); + private readonly bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL; public Array32 VertexAttributeDescriptions; public Array33 VertexBindingDescriptions; + public Array16 Viewports; + public Array16 Scissors; public Array8 ColorBlendAttachmentState; public Array9 AttachmentFormats; public uint AttachmentIntegerFormatMask; - public bool LogicOpsAllowed; public readonly override bool Equals(object obj) { @@ -42,7 +44,7 @@ namespace Ryujinx.Graphics.Vulkan { if (!Unsafe.As>(ref Id0).Equals(Unsafe.As>(ref other.Id0)) || !Unsafe.As>(ref Id4).Equals(Unsafe.As>(ref other.Id4)) || - !Unsafe.As>(ref Id7).Equals(Unsafe.As>(ref other.Id7))) + !Unsafe.As>(ref Id8).Equals(Unsafe.As>(ref other.Id8))) { return false; } @@ -85,7 +87,8 @@ namespace Ryujinx.Graphics.Vulkan Id5 * 23 ^ Id6 * 23 ^ Id7 * 23 ^ - Id8 * 23; + Id8 * 23 ^ + Id9 * 23; for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index c9a546648..714cb2833 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries PipelineStatistics = flags, }; - gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError(); + gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError(); } var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true); diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs index a364c5716..9edea5788 100644 --- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs @@ -1,7 +1,5 @@ using Silk.NET.Vulkan; using System; -using System.Collections.Generic; -using System.Linq; namespace Ryujinx.Graphics.Vulkan { @@ -31,13 +29,10 @@ namespace Ryujinx.Graphics.Vulkan } } - private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags); - private readonly TextureView[] _textures; private readonly Auto _renderPass; private readonly HashTableSlim> _framebuffers; private readonly RenderPassCacheKey _key; - private readonly List _forcedFences; public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb) { @@ -110,7 +105,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = PipelineConverter.CreateSubpassDependency(gd); + var subpassDependency = PipelineConverter.CreateSubpassDependency(); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { @@ -125,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); _renderPass = new Auto(new DisposableRenderPass(gd.Api, device, renderPass)); } @@ -143,8 +138,6 @@ namespace Ryujinx.Graphics.Vulkan _textures = textures; _key = key; - - _forcedFences = new List(); } public Auto GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb) @@ -166,37 +159,6 @@ namespace Ryujinx.Graphics.Vulkan return _renderPass; } - public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags) - { - if (!_forcedFences.Any(fence => fence.Texture == storage)) - { - _forcedFences.Add(new ForcedFence(storage, stageFlags)); - } - } - - public void InsertForcedFences(CommandBufferScoped cbs) - { - if (_forcedFences.Count > 0) - { - _forcedFences.RemoveAll((entry) => - { - if (entry.Texture.Disposed) - { - return true; - } - - entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags); - - return false; - }); - } - } - - public bool ContainsAttachment(TextureStorage storage) - { - return _textures.Any(view => view.Storage == storage); - } - public void Dispose() { // Dispose all framebuffers. diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs b/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs deleted file mode 100644 index f96b4a845..000000000 --- a/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Silk.NET.Vulkan; -using System; -using System.Diagnostics; - -namespace Ryujinx.Graphics.Vulkan -{ - class ResourceArray : IDisposable - { - private DescriptorSet[] _cachedDescriptorSets; - - private ShaderCollection _cachedDscProgram; - private int _cachedDscSetIndex; - private int _cachedDscIndex; - - private int _bindCount; - - protected void SetDirty(VulkanRenderer gd, bool isImage) - { - ReleaseDescriptorSet(); - - if (_bindCount != 0) - { - if (isImage) - { - gd.PipelineInternal.ForceImageDirty(); - } - else - { - gd.PipelineInternal.ForceTextureDirty(); - } - } - } - - public bool TryGetCachedDescriptorSets(CommandBufferScoped cbs, ShaderCollection program, int setIndex, out DescriptorSet[] sets) - { - if (_cachedDescriptorSets != null) - { - _cachedDscProgram.UpdateManualDescriptorSetCollectionOwnership(cbs, _cachedDscSetIndex, _cachedDscIndex); - - sets = _cachedDescriptorSets; - - return true; - } - - var dsc = program.GetNewManualDescriptorSetCollection(cbs, setIndex, out _cachedDscIndex).Get(cbs); - - sets = dsc.GetSets(); - - _cachedDescriptorSets = sets; - _cachedDscProgram = program; - _cachedDscSetIndex = setIndex; - - return false; - } - - public void IncrementBindCount() - { - _bindCount++; - } - - public void DecrementBindCount() - { - int newBindCount = --_bindCount; - Debug.Assert(newBindCount >= 0); - } - - private void ReleaseDescriptorSet() - { - if (_cachedDescriptorSets != null) - { - _cachedDscProgram.ReleaseManualDescriptorSetCollection(_cachedDscSetIndex, _cachedDscIndex); - _cachedDescriptorSets = null; - } - } - - public void Dispose() - { - ReleaseDescriptorSet(); - } - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs index 730a0a2f9..76a5ef4f9 100644 --- a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs +++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) + public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) { int setIndex = type switch { @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan }; _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); - _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); return this; } diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index aae28733f..f6a7be91e 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -15,7 +15,6 @@ - diff --git a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs index 7f37ab139..f67daeecc 100644 --- a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt; } - gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError(); + gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError(); _sampler = new Auto(new DisposableSampler(gd.Api, device, sampler)); } diff --git a/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs b/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs new file mode 100644 index 000000000..618a7d488 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs @@ -0,0 +1,60 @@ +using Silk.NET.Vulkan; +using System; +using System.Threading; +using VkSemaphore = Silk.NET.Vulkan.Semaphore; + +namespace Ryujinx.Graphics.Vulkan +{ + class SemaphoreHolder : IDisposable + { + private readonly Vk _api; + private readonly Device _device; + private VkSemaphore _semaphore; + private int _referenceCount; + private bool _disposed; + + public unsafe SemaphoreHolder(Vk api, Device device) + { + _api = api; + _device = device; + + var semaphoreCreateInfo = new SemaphoreCreateInfo + { + SType = StructureType.SemaphoreCreateInfo, + }; + + api.CreateSemaphore(device, in semaphoreCreateInfo, null, out _semaphore).ThrowOnError(); + + _referenceCount = 1; + } + + public VkSemaphore GetUnsafe() + { + return _semaphore; + } + + public VkSemaphore Get() + { + Interlocked.Increment(ref _referenceCount); + return _semaphore; + } + + public unsafe void Put() + { + if (Interlocked.Decrement(ref _referenceCount) == 0) + { + _api.DestroySemaphore(_device, _semaphore, null); + _semaphore = default; + } + } + + public void Dispose() + { + if (!_disposed) + { + Put(); + _disposed = true; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs index 1c8caffd9..06f3499db 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan PCode = (uint*)pCode, }; - api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError(); + api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError(); } CompileStatus = ProgramLinkStatus.Success; diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index c9aab4018..178546983 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -23,13 +23,8 @@ namespace Ryujinx.Graphics.Vulkan public bool IsCompute { get; } public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0; - public bool UpdateTexturesWithoutTemplate { get; } - public uint Stages { get; } - public PipelineStageFlags IncoherentBufferWriteStages { get; } - public PipelineStageFlags IncoherentTextureWriteStages { get; } - public ResourceBindingSegment[][] ClearSegments { get; } public ResourceBindingSegment[][] BindingSegments { get; } public DescriptorSetTemplate[] Templates { get; } @@ -132,12 +127,8 @@ namespace Ryujinx.Graphics.Vulkan Stages = stages; ClearSegments = BuildClearSegments(sets); - BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures); + BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); Templates = BuildTemplates(usePushDescriptors); - (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages); - - // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. - UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures; _compileTask = Task.CompletedTask; _firstBackgroundUse = false; @@ -289,10 +280,8 @@ namespace Ryujinx.Graphics.Vulkan return segments; } - private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection setUsages, out bool usesBufferTextures) + private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection setUsages) { - usesBufferTextures = false; - ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) @@ -306,11 +295,6 @@ namespace Ryujinx.Graphics.Vulkan { ResourceUsage usage = setUsages[setIndex].Usages[index]; - if (usage.Type == ResourceType.BufferTexture) - { - usesBufferTextures = true; - } - if (currentUsage.Binding + currentCount != usage.Binding || currentUsage.Type != usage.Type || currentUsage.Stages != usage.Stages || @@ -381,73 +365,6 @@ namespace Ryujinx.Graphics.Vulkan return templates; } - private PipelineStageFlags GetPipelineStages(ResourceStages stages) - { - PipelineStageFlags result = 0; - - if ((stages & ResourceStages.Compute) != 0) - { - result |= PipelineStageFlags.ComputeShaderBit; - } - - if ((stages & ResourceStages.Vertex) != 0) - { - result |= PipelineStageFlags.VertexShaderBit; - } - - if ((stages & ResourceStages.Fragment) != 0) - { - result |= PipelineStageFlags.FragmentShaderBit; - } - - if ((stages & ResourceStages.Geometry) != 0) - { - result |= PipelineStageFlags.GeometryShaderBit; - } - - if ((stages & ResourceStages.TessellationControl) != 0) - { - result |= PipelineStageFlags.TessellationControlShaderBit; - } - - if ((stages & ResourceStages.TessellationEvaluation) != 0) - { - result |= PipelineStageFlags.TessellationEvaluationShaderBit; - } - - return result; - } - - private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection setUsages) - { - PipelineStageFlags buffer = PipelineStageFlags.None; - PipelineStageFlags texture = PipelineStageFlags.None; - - foreach (var set in setUsages) - { - foreach (var range in set.Usages) - { - if (range.Write) - { - PipelineStageFlags stages = GetPipelineStages(range.Stages); - - switch (range.Type) - { - case ResourceType.Image: - texture |= stages; - break; - case ResourceType.StorageBuffer: - case ResourceType.BufferImage: - buffer |= stages; - break; - } - } - } - } - - return (buffer, texture); - } - private async Task BackgroundCompilation() { await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); @@ -687,21 +604,6 @@ namespace Ryujinx.Graphics.Vulkan return _plce.GetNewDescriptorSetCollection(setIndex, out isNew); } - public Auto GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex) - { - return _plce.GetNewManualDescriptorSetCollection(cbs, setIndex, out cacheIndex); - } - - public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex) - { - _plce.UpdateManualDescriptorSetCollectionOwnership(cbs, setIndex, cacheIndex); - } - - public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex) - { - _plce.ReleaseManualDescriptorSetCollection(setIndex, cacheIndex); - } - public bool HasSameLayout(ShaderCollection other) { return other != null && _plce == other._plce; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureArray.cs b/src/Ryujinx.Graphics.Vulkan/TextureArray.cs index 99238b1f5..6ef9087bc 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureArray.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureArray.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Vulkan { - class TextureArray : ResourceArray, ITextureArray + class TextureArray : ITextureArray { private readonly VulkanRenderer _gd; @@ -29,6 +29,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly bool _isBuffer; + public bool Bound; + public TextureArray(VulkanRenderer gd, int size, bool isBuffer) { _gd = gd; @@ -104,7 +106,8 @@ namespace Ryujinx.Graphics.Vulkan { _cachedCommandBufferIndex = -1; _storages = null; - SetDirty(_gd, isImage: false); + + _gd.PipelineInternal.ForceTextureDirty(); } public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags) @@ -187,48 +190,5 @@ namespace Ryujinx.Graphics.Vulkan return bufferTextures; } - - public DescriptorSet[] GetDescriptorSets( - Device device, - CommandBufferScoped cbs, - DescriptorSetTemplateUpdater templateUpdater, - ShaderCollection program, - int setIndex, - TextureView dummyTexture, - SamplerHolder dummySampler) - { - if (TryGetCachedDescriptorSets(cbs, program, setIndex, out DescriptorSet[] sets)) - { - // We still need to ensure the current command buffer holds a reference to all used textures. - - if (!_isBuffer) - { - GetImageInfos(_gd, cbs, dummyTexture, dummySampler); - } - else - { - GetBufferViews(cbs); - } - - return sets; - } - - DescriptorSetTemplate template = program.Templates[setIndex]; - - DescriptorSetTemplateWriter tu = templateUpdater.Begin(template); - - if (!_isBuffer) - { - tu.Push(GetImageInfos(_gd, cbs, dummyTexture, dummySampler)); - } - else - { - tu.Push(GetBufferViews(cbs)); - } - - templateUpdater.Commit(_gd, device, sets[0]); - - return sets; - } } } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index 073eee2ca..e0694b197 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -1,7 +1,7 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; +using System.Buffers; using System.Collections.Generic; using Format = Ryujinx.Graphics.GAL.Format; using VkFormat = Silk.NET.Vulkan.Format; @@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan private int _offset; private int _size; private Auto _bufferView; + private Dictionary> _selfManagedViews; private int _bufferCount; @@ -79,25 +80,35 @@ namespace Ryujinx.Graphics.Vulkan private void ReleaseImpl() { + if (_selfManagedViews != null) + { + foreach (var bufferView in _selfManagedViews.Values) + { + bufferView.Dispose(); + } + + _selfManagedViews = null; + } + _bufferView?.Dispose(); _bufferView = null; } /// - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { - _gd.SetBufferData(_bufferHandle, _offset, data.Span); + _gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span); data.Dispose(); } /// - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { throw new NotSupportedException(); } /// - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } @@ -126,5 +137,28 @@ namespace Ryujinx.Graphics.Vulkan return _bufferView?.Get(cbs, _offset, _size, write).Value ?? default; } + + public BufferView GetBufferView(CommandBufferScoped cbs, Format format, bool write) + { + var vkFormat = FormatTable.GetFormat(format); + if (vkFormat == VkFormat) + { + return GetBufferView(cbs, write); + } + + if (_selfManagedViews != null && _selfManagedViews.TryGetValue(format, out var bufferView)) + { + return bufferView.Get(cbs, _offset, _size, write).Value; + } + + bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl); + + if (bufferView != null) + { + (_selfManagedViews ??= new Dictionary>()).Add(format, bufferView); + } + + return bufferView?.Get(cbs, _offset, _size, write).Value ?? default; + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs index 45cddd772..7c06a5df6 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan DstOffsets = dstOffsets, }; - api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter); + api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter); copySrcLevel++; copyDstLevel++; @@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan { var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent); - api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region); + api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region); } else { var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent); - api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region); + api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region); } width = Math.Max(1, width >> 1); @@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan ImageLayout.General, ImageLayout.General); - var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd); + var subpassDependency = PipelineConverter.CreateSubpassDependency2(); fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) { @@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); using var rp = new Auto(new DisposableRenderPass(gd.Api, device, renderPass)); @@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan Layers = (uint)src.Layers, }; - gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); + gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); using var fb = new Auto(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView); var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height)); @@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan // to resolve the depth-stencil texture. // TODO: Do speculative resolve and part of the same render pass as the draw to avoid // ending the current render pass? - gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline); + gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); gd.Api.CmdEndRenderPass(cbs.CommandBuffer); } } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 10b36a3f9..230dbd4e8 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -4,7 +4,6 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Numerics; -using System.Runtime.CompilerServices; using Format = Ryujinx.Graphics.GAL.Format; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -13,11 +12,6 @@ namespace Ryujinx.Graphics.Vulkan { class TextureStorage : IDisposable { - private struct TextureSliceInfo - { - public int BindCount; - } - private const MemoryPropertyFlags DefaultImageMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -44,12 +38,9 @@ namespace Ryujinx.Graphics.Vulkan public TextureCreateInfo Info => _info; - public bool Disposed { get; private set; } - private readonly Image _image; private readonly Auto _imageAuto; private readonly Auto _allocationAuto; - private readonly int _depthOrLayers; private Auto _foreignAllocationAuto; private Dictionary _aliasedStorages; @@ -62,9 +53,6 @@ namespace Ryujinx.Graphics.Vulkan private int _viewsCount; private readonly ulong _size; - private int _bindCount; - private readonly TextureSliceInfo[] _slices; - public VkFormat VkFormat { get; } public unsafe TextureStorage( @@ -83,7 +71,6 @@ namespace Ryujinx.Graphics.Vulkan var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); VkFormat = format; - _depthOrLayers = info.GetDepthOrLayers(); var type = info.Target.Convert(); @@ -91,9 +78,9 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); - var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; + var flags = ImageCreateFlags.CreateMutableFormatBit; // This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube. bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray; @@ -125,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan Flags = flags, }; - gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError(); + gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError(); if (foreignAllocation == null) { @@ -159,8 +146,6 @@ namespace Ryujinx.Graphics.Vulkan InitialTransition(ImageLayout.Preinitialized, ImageLayout.General); } - - _slices = new TextureSliceInfo[levels * _depthOrLayers]; } public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format) @@ -297,7 +282,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - in barrier); + barrier); if (useTempCbs) { @@ -305,7 +290,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities) + public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage) { var usage = DefaultUsageFlags; @@ -318,19 +303,11 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample; - if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } - if (capabilities.SupportsAttachmentFeedbackLoop && - (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0) - { - usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt; - } - return usage; } @@ -422,11 +399,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); } offset += mipSize; @@ -456,17 +433,6 @@ namespace Ryujinx.Graphics.Vulkan return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; } - public void AddStoreOpUsage(bool depthStencil) - { - _lastModificationStage = depthStencil ? - PipelineStageFlags.LateFragmentTestsBit : - PipelineStageFlags.ColorAttachmentOutputBit; - - _lastModificationAccess = depthStencil ? - AccessFlags.DepthStencilAttachmentWriteBit : - AccessFlags.ColorAttachmentWriteBit; - } - public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil) { PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage; @@ -492,7 +458,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags); + _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags); _lastReadStage = PipelineStageFlags.None; _lastReadAccess = AccessFlags.None; @@ -525,61 +491,12 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags); + _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags); _lastModificationAccess = AccessFlags.None; } } - public void AddBinding(TextureView view) - { - // Assumes a view only has a first level. - - int index = view.FirstLevel * _depthOrLayers + view.FirstLayer; - int layers = view.Layers; - - for (int i = 0; i < layers; i++) - { - ref TextureSliceInfo info = ref _slices[index++]; - - info.BindCount++; - } - - _bindCount++; - } - - public void ClearBindings() - { - if (_bindCount != 0) - { - Array.Clear(_slices, 0, _slices.Length); - - _bindCount = 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsBound(TextureView view) - { - if (_bindCount != 0) - { - int index = view.FirstLevel * _depthOrLayers + view.FirstLayer; - int layers = view.Layers; - - for (int i = 0; i < layers; i++) - { - ref TextureSliceInfo info = ref _slices[index++]; - - if (info.BindCount != 0) - { - return true; - } - } - } - - return false; - } - public void IncrementViewsCount() { _viewsCount++; @@ -597,8 +514,6 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { - Disposed = true; - if (_aliasedStorages != null) { foreach (var storage in _aliasedStorages.Values) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index b7b936809..f2aaf4693 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -1,7 +1,7 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto _imageView2dArray; private Dictionary _selfManagedViews; - private int _hazardUses; - private readonly TextureCreateInfo _info; private HashTableSlim _renderPasses; @@ -62,7 +60,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); - var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities); + var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); @@ -102,7 +100,7 @@ namespace Ryujinx.Graphics.Vulkan unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags) { - var imageViewUsage = new ImageViewUsageCreateInfo + var usage = new ImageViewUsageCreateInfo { SType = StructureType.ImageViewUsageCreateInfo, Usage = usageFlags, @@ -116,16 +114,16 @@ namespace Ryujinx.Graphics.Vulkan Format = format, Components = cm, SubresourceRange = sr, - PNext = &imageViewUsage, + PNext = &usage, }; - gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError(); + gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); return new Auto(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage()); } ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit; - if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample())) + if (info.Format.IsImageCompatible()) { shaderUsage |= ImageUsageFlags.StorageBit; } @@ -156,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan } else { - subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, 1, (uint)firstLayer, (uint)info.Depth); + subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage); } @@ -494,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan dstStageMask, DependencyFlags.None, 1, - in memoryBarrier, + memoryBarrier, 0, null, 0, @@ -559,7 +557,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - in memoryBarrier); + memoryBarrier); } public TextureView GetView(Format format) @@ -669,36 +667,8 @@ 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); @@ -707,19 +677,6 @@ 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 GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer) @@ -746,23 +703,23 @@ namespace Ryujinx.Graphics.Vulkan } /// - public void SetData(MemoryOwner data) + public void SetData(IMemoryOwner data) { - SetData(data.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false); + SetData(data.Memory.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false); data.Dispose(); } /// - public void SetData(MemoryOwner data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { - SetData(data.Span, layer, level, 1, 1, singleSlice: true); + SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); data.Dispose(); } /// - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { - SetData(data.Span, layer, level, 1, 1, singleSlice: true, region); + SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true, region); data.Dispose(); } @@ -951,11 +908,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); } offset += mipSize; @@ -1012,11 +969,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); } } @@ -1036,35 +993,7 @@ namespace Ryujinx.Graphics.Vulkan throw new NotImplementedException(); } - public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List feedbackLoopHazards) - { - Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags); - - if (feedbackLoopHazards != null && Storage.IsBound(this)) - { - feedbackLoopHazards.Add(this); - _hazardUses++; - } - } - - public void ClearUsage(List feedbackLoopHazards) - { - if (_hazardUses != 0 && feedbackLoopHazards != null) - { - feedbackLoopHazards.Remove(this); - _hazardUses--; - } - } - - public void DecrementHazardUses() - { - if (_hazardUses != 0) - { - _hazardUses--; - } - } - - public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( + public (Auto renderPass, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, CommandBufferScoped cbs, @@ -1077,7 +1006,7 @@ namespace Ryujinx.Graphics.Vulkan rpHolder = new RenderPassHolder(gd, device, key, fb); } - return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb)); + return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb)); } public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass) diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs index 55ae0cd81..e0f569079 100644 --- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -69,30 +69,27 @@ namespace Ryujinx.Graphics.Vulkan { DriverId.AmdProprietary => "AMD", DriverId.AmdOpenSource => "AMD (Open)", - DriverId.MesaRadv => "RADV", - DriverId.NvidiaProprietary => "NVIDIA", - DriverId.IntelProprietaryWindows => "Intel", - DriverId.IntelOpenSourceMesa => "Intel (Open)", - DriverId.ImaginationProprietary => "Imagination", - DriverId.QualcommProprietary => "Qualcomm", DriverId.ArmProprietary => "ARM", - DriverId.GoogleSwiftshader => "SwiftShader", - DriverId.GgpProprietary => "GGP", DriverId.BroadcomProprietary => "Broadcom", - DriverId.MesaLlvmpipe => "LLVMpipe", - DriverId.Moltenvk => "MoltenVK", DriverId.CoreaviProprietary => "CoreAVI", + DriverId.GgpProprietary => "GGP", + DriverId.GoogleSwiftshader => "SwiftShader", + DriverId.ImaginationProprietary => "Imagination", + DriverId.IntelOpenSourceMesa => "Intel (Open)", + DriverId.IntelProprietaryWindows => "Intel", DriverId.JuiceProprietary => "Juice", - DriverId.VerisiliconProprietary => "Verisilicon", + DriverId.MesaDozen => "Dozen", + DriverId.MesaLlvmpipe => "LLVMpipe", + DriverId.MesaPanvk => "PanVK", + DriverId.MesaRadv => "RADV", DriverId.MesaTurnip => "Turnip", DriverId.MesaV3DV => "V3DV", - DriverId.MesaPanvk => "PanVK", - DriverId.SamsungProprietary => "Samsung", DriverId.MesaVenus => "Venus", - DriverId.MesaDozen => "Dozen", - DriverId.MesaNvk => "NVK", - DriverId.ImaginationOpenSourceMesa => "Imagination (Open)", - DriverId.MesaAgxv => "Honeykrisp", + DriverId.Moltenvk => "MoltenVK", + DriverId.NvidiaProprietary => "NVIDIA", + DriverId.QualcommProprietary => "Qualcomm", + DriverId.SamsungProprietary => "Samsung", + DriverId.VerisiliconProprietary => "Verisilicon", _ => id.ToString(), }; } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 2c327fdb7..d59ca7e0e 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -42,10 +42,6 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_depth_clip_control", "VK_KHR_portability_subset", // As per spec, we should enable this if present. "VK_EXT_4444_formats", - "VK_KHR_8bit_storage", - "VK_KHR_maintenance2", - "VK_EXT_attachment_feedback_loop_layout", - "VK_EXT_attachment_feedback_loop_dynamic_state", }; private static readonly string[] _requiredExtensions = { @@ -359,36 +355,6 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesDepthClipControl; } - PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, - PNext = features2.PNext, - }; - - if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout")) - { - features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout; - } - - PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, - PNext = features2.PNext, - }; - - if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state")) - { - features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout; - } - - PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() - { - SType = StructureType.PhysicalDeviceVulkan12Features, - PNext = features2.PNext, - }; - - features2.PNext = &supportedPhysicalDeviceVulkan12Features; - api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); var supportedFeatures = features2.Features; @@ -416,7 +382,6 @@ namespace Ryujinx.Graphics.Vulkan TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, RobustBufferAccess = useRobustBufferAccess, - SampleRateShading = supportedFeatures.SampleRateShading, }; void* pExtendedFeatures = null; @@ -486,11 +451,9 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan12Features, PNext = pExtendedFeatures, - DescriptorIndexing = supportedPhysicalDeviceVulkan12Features.DescriptorIndexing, - DrawIndirectCount = supportedPhysicalDeviceVulkan12Features.DrawIndirectCount, - UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout, - UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess, - StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess, + DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"), + DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName), + UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout"), }; pExtendedFeatures = &featuresVk12; @@ -555,36 +518,6 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresDepthClipControl; } - PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout; - - if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") && - supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout) - { - featuresAttachmentFeedbackLoopLayout = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, - PNext = pExtendedFeatures, - AttachmentFeedbackLoopLayout = true, - }; - - pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout; - } - - PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout; - - if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") && - supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState) - { - featuresDynamicAttachmentFeedbackLoopLayout = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, - PNext = pExtendedFeatures, - AttachmentFeedbackLoopDynamicState = true, - }; - - pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout; - } - var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 0faaec82a..8ef05de36 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -38,7 +38,6 @@ namespace Ryujinx.Graphics.Vulkan internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } - internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; } internal uint QueueFamilyIndex { get; private set; } internal Queue Queue { get; private set; } @@ -88,11 +87,9 @@ namespace Ryujinx.Graphics.Vulkan internal bool IsAmdGcn { get; private set; } internal bool IsNvidiaPreTuring { get; private set; } internal bool IsIntelArc { get; private set; } - internal bool IsQualcommProprietary { get; private set; } internal bool IsMoltenVk { get; private set; } internal bool IsTBDR { get; private set; } internal bool IsSharedMemory { get; private set; } - public string GpuVendor { get; private set; } public string GpuDriver { get; private set; } public string GpuRenderer { get; private set; } @@ -150,11 +147,6 @@ namespace Ryujinx.Graphics.Vulkan DrawIndirectCountApi = drawIndirectCountApi; } - if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi)) - { - DynamicFeedbackLoopApi = dynamicFeedbackLoopApi; - } - if (maxQueueCount >= 2) { Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); @@ -249,16 +241,6 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, }; - PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, - }; - - PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new() - { - SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, - }; - PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new() { SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr, @@ -295,22 +277,6 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresDepthClipControl; } - bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"); - - if (supportsAttachmentFeedbackLoop) - { - featuresAttachmentFeedbackLoop.PNext = features2.PNext; - features2.PNext = &featuresAttachmentFeedbackLoop; - } - - bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"); - - if (supportsDynamicAttachmentFeedbackLoop) - { - featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext; - features2.PNext = &featuresDynamicAttachmentFeedbackLoop; - } - bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); if (usePortability) @@ -378,7 +344,7 @@ namespace Ryujinx.Graphics.Vulkan { IsNvidiaPreTuring = gpuNumber < 2000; } - else if (GpuRenderer.Contains("TITAN") && !GpuRenderer.Contains("RTX")) + else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX")) { IsNvidiaPreTuring = true; } @@ -388,8 +354,6 @@ namespace Ryujinx.Graphics.Vulkan IsIntelArc = GpuRenderer.StartsWith("Intel(R) Arc(TM)"); } - IsQualcommProprietary = hasDriverProperties && driverProperties.DriverID == DriverId.QualcommProprietary; - ulong minResourceAlignment = Math.Max( Math.Max( properties.Limits.MinStorageBufferOffsetAlignment, @@ -433,8 +397,6 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, - supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout, - supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState, propertiesSubgroup.SubgroupSize, supportedSampleCounts, portabilityFlags, @@ -449,7 +411,7 @@ namespace Ryujinx.Graphics.Vulkan Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi); HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device); - CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary); + CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); PipelineLayoutCache = new PipelineLayoutCache(); @@ -524,7 +486,12 @@ namespace Ryujinx.Graphics.Vulkan public BufferHandle CreateBuffer(int size, BufferAccess access) { - return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), access.HasFlag(BufferAccess.Stream)); + return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream); + } + + public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) + { + return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), storageHint); } public BufferHandle CreateBuffer(nint pointer, int size) @@ -708,25 +675,11 @@ namespace Ryujinx.Graphics.Vulkan var limits = _physicalDevice.PhysicalDeviceProperties.Limits; var mainQueueProperties = _physicalDevice.QueueFamilyProperties[QueueFamilyIndex]; - SystemMemoryType memoryType; - - if (IsSharedMemory) - { - memoryType = SystemMemoryType.UnifiedMemory; - } - else - { - memoryType = Vendor == Vendor.Nvidia ? - SystemMemoryType.DedicatedMemorySlowStorage : - SystemMemoryType.DedicatedMemory; - } - return new Capabilities( api: TargetApi.Vulkan, GpuVendor, - memoryType: memoryType, hasFrontFacingBug: IsIntelWindows, - hasVectorIndexingBug: IsQualcommProprietary, + hasVectorIndexingBug: Vendor == Vendor.Qualcomm, needsFragmentOutputSpecialization: IsMoltenVk, reduceShaderPrecision: IsMoltenVk, supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats, @@ -766,12 +719,6 @@ namespace Ryujinx.Graphics.Vulkan supportsViewportSwizzle: false, supportsIndirectParameters: true, supportsDepthClipControl: Capabilities.SupportsDepthClipControl, - uniformBufferSetIndex: PipelineBase.UniformSetIndex, - storageBufferSetIndex: PipelineBase.StorageSetIndex, - textureSetIndex: PipelineBase.TextureSetIndex, - imageSetIndex: PipelineBase.ImageSetIndex, - extraSetBaseIndex: PipelineBase.DescriptorSetLayouts, - maximumExtraSets: Math.Max(0, (int)limits.MaxBoundDescriptorSets - PipelineBase.DescriptorSetLayouts), maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, @@ -781,26 +728,7 @@ namespace Ryujinx.Graphics.Vulkan shaderSubgroupSize: (int)Capabilities.SubgroupSize, storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment, - gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0, - maximumGpuMemory: GetTotalGPUMemory()); - } - - private ulong GetTotalGPUMemory() - { - ulong totalMemory = 0; - - Api.GetPhysicalDeviceMemoryProperties(_physicalDevice.PhysicalDevice, out PhysicalDeviceMemoryProperties memoryProperties); - - for (int i = 0; i < memoryProperties.MemoryHeapCount; i++) - { - var heap = memoryProperties.MemoryHeaps[i]; - if ((heap.Flags & MemoryHeapFlags.DeviceLocalBit) == MemoryHeapFlags.DeviceLocalBit) - { - totalMemory += heap.Size; - } - } - - return totalMemory; + gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0); } public HardwareInfo GetHardwareInfo() @@ -884,7 +812,6 @@ namespace Ryujinx.Graphics.Vulkan private void PrintGpuInformation() { Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); - Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB"); } public void Initialize(GraphicsDebugLevel logLevel) @@ -993,11 +920,6 @@ namespace Ryujinx.Graphics.Vulkan ScreenCaptured?.Invoke(this, bitmap); } - public bool SupportsRenderPassBarrier(PipelineStageFlags flags) - { - return !(IsMoltenVk || IsQualcommProprietary); - } - public unsafe void Dispose() { if (!_initialized) diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 3dc6d4e19..a4ac9e9f1 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan SwizzleComponent.Blue, SwizzleComponent.Alpha); - _gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError(); + _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError(); _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null); @@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < _imageAvailableSemaphores.Length; i++) { - _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError(); + _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError(); } _renderFinishedSemaphores = new Semaphore[imageCount]; for (int i = 0; i < _renderFinishedSemaphores.Length; i++) { - _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError(); + _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError(); } } @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan SubresourceRange = subresourceRange, }; - _gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError(); + _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError(); return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format); } @@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan lock (_gd.QueueLock) { - _gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo); + _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo); } } @@ -568,13 +568,6 @@ namespace Ryujinx.Graphics.Vulkan _scalingFilter.Level = _scalingFilterLevel; break; - case ScalingFilter.Area: - if (_scalingFilter is not AreaScalingFilter) - { - _scalingFilter?.Dispose(); - _scalingFilter = new AreaScalingFilter(_gd, _device); - } - break; } } } @@ -618,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - in barrier); + barrier); } private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) @@ -630,8 +623,7 @@ namespace Ryujinx.Graphics.Vulkan public override void SetSize(int width, int height) { - // We don't need to use width and height as we can get the size from the surface. - _swapchainIsDirty = true; + // Not needed as we can get the size from the surface. } public override void ChangeVSyncMode(bool vsyncEnabled) diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs index 2d350374b..f69934313 100644 --- a/src/Ryujinx.Gtk3/Program.cs +++ b/src/Ryujinx.Gtk3/Program.cs @@ -4,17 +4,15 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.UI; -using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; 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; @@ -42,6 +40,9 @@ namespace Ryujinx [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); + [LibraryImport("libc", SetLastError = true)] + private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); + private const uint MbIconWarning = 0x30; static Program() @@ -103,13 +104,12 @@ namespace Ryujinx throw new NotSupportedException("Failed to initialize multi-threading support."); } - OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11"); + Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); + setenv("GDK_BACKEND", "x11", 1); } if (OperatingSystem.IsMacOS()) { - MVKInitialization.InitializeResolver(); - string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); string resourcesDataDir; @@ -122,13 +122,19 @@ namespace Ryujinx resourcesDataDir = baseDirectory; } + static void SetEnvironmentVariableNoCaching(string key, string value) + { + int res = setenv(key, value, 1); + Debug.Assert(res != -1); + } + // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); + SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); + SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); - OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); + SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); } string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); @@ -155,6 +161,12 @@ 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); @@ -169,24 +181,21 @@ namespace Ryujinx { // No configuration, we load the default values and save it to disk ConfigurationPath = appDataConfigurationPath; - Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}"); ConfigurationState.Instance.LoadDefault(); ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath); } else { - Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}"); - if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat)) { ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath); } else { - Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}"); - ConfigurationState.Instance.LoadDefault(); + + Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}"); } } @@ -224,9 +233,9 @@ namespace Ryujinx // Logging system information. PrintSystemInfo(); - // Enable OGL multithreading on the driver, and some other flags. + // Enable OGL multithreading on the driver, when available. BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off); + DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off); // Initialize Gtk. Application.Init(); @@ -243,12 +252,6 @@ 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; @@ -316,35 +319,7 @@ namespace Ryujinx if (CommandLineState.LaunchPathArg != null) { - if (mainWindow.ApplicationLibrary.TryGetApplicationsFromFile(CommandLineState.LaunchPathArg, out List applications)) - { - ApplicationData applicationData; - - if (CommandLineState.LaunchApplicationId != null) - { - applicationData = applications.Find(application => application.IdString == CommandLineState.LaunchApplicationId); - - if (applicationData != null) - { - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find requested application id '{CommandLineState.LaunchApplicationId}' in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } - } - else - { - applicationData = applications[0]; - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find any application in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } + mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); } if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj index 722d6080b..b4453f9d7 100644 --- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj +++ b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj @@ -30,6 +30,7 @@ + diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.cs b/src/Ryujinx.Gtk3/UI/MainWindow.cs index b10dfe3f9..d1ca6ce6a 100644 --- a/src/Ryujinx.Gtk3/UI/MainWindow.cs +++ b/src/Ryujinx.Gtk3/UI/MainWindow.cs @@ -37,9 +37,7 @@ using Ryujinx.UI.Windows; using Silk.NET.Vulkan; using SPB.Graphics.Vulkan; using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Reflection; using System.Threading; @@ -62,6 +60,7 @@ namespace Ryujinx.UI private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; + private readonly ApplicationLibrary _applicationLibrary; private readonly GtkHostUIHandler _uiHandler; private readonly AutoResetEvent _deviceExitStatus; private readonly ListStore _tableStore; @@ -70,12 +69,11 @@ namespace Ryujinx.UI private bool _gameLoaded; private bool _ending; - private ApplicationData _currentApplicationData = null; + private string _currentEmulatedGamePath = null; private string _lastScannedAmiiboId = ""; private bool _lastScannedAmiiboShowAll = false; - public readonly ApplicationLibrary ApplicationLibrary; public RendererWidgetBase RendererWidget; public InputManager InputManager; @@ -182,15 +180,8 @@ namespace Ryujinx.UI _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); _userChannelPersistence = new UserChannelPersistence(); - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - // Instantiate GUI objects. - ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel) - { - DesiredLanguage = ConfigurationState.Instance.System.Language, - }; + _applicationLibrary = new ApplicationLibrary(_virtualFileSystem); _uiHandler = new GtkHostUIHandler(this); _deviceExitStatus = new AutoResetEvent(false); @@ -199,8 +190,8 @@ namespace Ryujinx.UI FocusInEvent += MainWindow_FocusInEvent; FocusOutEvent += MainWindow_FocusOutEvent; - ApplicationLibrary.ApplicationAdded += Application_Added; - ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; + _applicationLibrary.ApplicationAdded += Application_Added; + _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; _fileMenu.StateChanged += FileMenu_StateChanged; _actionMenu.StateChanged += ActionMenu_StateChanged; @@ -328,6 +319,7 @@ 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) => { @@ -647,7 +639,7 @@ namespace Ryujinx.UI } var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value - ? HLE.MemoryConfiguration.MemoryConfiguration8GiB + ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; @@ -740,8 +732,7 @@ namespace Ryujinx.UI Thread applicationLibraryThread = new(() => { - ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs); + _applicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); _updatingGameTable = false; }) @@ -792,7 +783,7 @@ namespace Ryujinx.UI } } - private bool LoadApplication(string path, ulong applicationId, bool isFirmwareTitle) + private bool LoadApplication(string path, bool isFirmwareTitle) { SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); @@ -866,7 +857,7 @@ namespace Ryujinx.UI case ".xci": Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - return _emulationContext.LoadXci(path, applicationId); + return _emulationContext.LoadXci(path); case ".nca": Logger.Info?.Print(LogClass.Application, "Loading as NCA."); @@ -875,7 +866,7 @@ namespace Ryujinx.UI case ".pfs0": Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - return _emulationContext.LoadNsp(path, applicationId); + return _emulationContext.LoadNsp(path); default: Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); try @@ -896,7 +887,7 @@ namespace Ryujinx.UI return false; } - public void RunApplication(ApplicationData application, bool startFullscreen = false) + public void RunApplication(string path, bool startFullscreen = false) { if (_gameLoaded) { @@ -918,14 +909,14 @@ namespace Ryujinx.UI bool isFirmwareTitle = false; - if (application.Path.StartsWith("@SystemContent")) + if (path.StartsWith("@SystemContent")) { - application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path); + path = VirtualFileSystem.SwitchPathToSystemPath(path); isFirmwareTitle = true; } - if (!LoadApplication(application.Path, application.Id, isFirmwareTitle)) + if (!LoadApplication(path, isFirmwareTitle)) { _emulationContext.Dispose(); SwitchToGameTable(); @@ -935,7 +926,7 @@ namespace Ryujinx.UI SetupProgressUIHandlers(); - _currentApplicationData = application; + _currentEmulatedGamePath = path; _deviceExitStatus.Reset(); @@ -1174,7 +1165,7 @@ namespace Ryujinx.UI _tableStore.AppendValues( args.AppData.Favorite, new Gdk.Pixbuf(args.AppData.Icon, 75, 75), - $"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}", + $"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}", args.AppData.Developer, args.AppData.Version, args.AppData.TimePlayedString, @@ -1262,22 +1253,9 @@ namespace Ryujinx.UI { _gameTableSelection.GetSelected(out TreeIter treeIter); - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; + string path = (string)_tableStore.GetValue(treeIter, 9); - RunApplication(application); + RunApplication(path); } private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args) @@ -1335,22 +1313,13 @@ namespace Ryujinx.UI return; } - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; + string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString(); + string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0]; + string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); - _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application); + BlitStruct controlData = (BlitStruct)_tableStore.GetValue(treeIter, 10); + + _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, titleFilePath, titleName, titleId, controlData); } private void Load_Application_File(object sender, EventArgs args) @@ -1372,15 +1341,7 @@ namespace Ryujinx.UI if (fileChooser.Run() == (int)ResponseType.Accept) { - if (ApplicationLibrary.TryGetApplicationsFromFile(fileChooser.Filename, - out List applications)) - { - RunApplication(applications[0]); - } - else - { - GtkDialog.CreateErrorDialog("No applications found in selected file."); - } + RunApplication(fileChooser.Filename); } } @@ -1390,13 +1351,7 @@ namespace Ryujinx.UI if (fileChooser.Run() == (int)ResponseType.Accept) { - ApplicationData applicationData = new() - { - Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename), - Path = fileChooser.Filename, - }; - - RunApplication(applicationData); + RunApplication(fileChooser.Filename); } } @@ -1411,14 +1366,7 @@ namespace Ryujinx.UI { string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); - ApplicationData applicationData = new() - { - Name = "miiEdit", - Id = 0x0100000000001009ul, - Path = contentPath, - }; - - RunApplication(applicationData); + RunApplication(contentPath); } private void Open_Ryu_Folder(object sender, EventArgs args) @@ -1698,13 +1646,13 @@ namespace Ryujinx.UI { _userChannelPersistence.ShouldRestart = false; - RunApplication(_currentApplicationData); + RunApplication(_currentEmulatedGamePath); } else { // otherwise, clear state. _userChannelPersistence = new UserChannelPersistence(); - _currentApplicationData = null; + _currentEmulatedGamePath = null; _actionMenu.Sensitive = false; _firmwareInstallFile.Sensitive = true; _firmwareInstallDirectory.Sensitive = true; @@ -1766,7 +1714,7 @@ namespace Ryujinx.UI _emulationContext.Processes.ActiveApplication.ProgramId, _emulationContext.Processes.ActiveApplication.ApplicationControlProperties .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), - _currentApplicationData.Path); + _currentEmulatedGamePath); window.Destroyed += CheatWindow_Destroyed; window.Show(); diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs index 12139e87d..0e636792d 100644 --- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs +++ b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs @@ -13,13 +13,16 @@ using Ryujinx.Input.HLE; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Widgets; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; 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; @@ -401,31 +404,23 @@ namespace Ryujinx.UI return; } - var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; - using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); + Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) + : Image.LoadPixelData(e.Data, e.Width, e.Height); - 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) + if (e.FlipX) { - 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); + image.Mutate(x => x.Flip(FlipMode.Horizontal)); } - canvas.DrawBitmap(image, new SKPoint()); - surface.Flush(); - using var snapshot = surface.Snapshot(); - using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80); - using var file = File.OpenWrite(path); - encoded.SaveTo(file); + if (e.FlipY) + { + image.Mutate(x => x.Flip(FlipMode.Vertical)); + } + + image.SaveAsPng(path, new PngEncoder() + { + ColorType = PngColorType.Rgb, + }); image.Dispose(); diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs index a3e3d4c8c..c8236223a 100644 --- a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs @@ -16,8 +16,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; @@ -25,6 +23,7 @@ using Ryujinx.UI.Windows; using System; using System.Buffers; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Reflection; using System.Threading; @@ -37,13 +36,17 @@ namespace Ryujinx.UI.Widgets private readonly VirtualFileSystem _virtualFileSystem; private readonly AccountManager _accountManager; private readonly HorizonClient _horizonClient; + private readonly BlitStruct _controlData; - private readonly ApplicationData _applicationData; + private readonly string _titleFilePath; + private readonly string _titleName; + private readonly string _titleIdText; + private readonly ulong _titleId; private MessageDialog _dialog; private bool _cancel; - public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData) + public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct controlData) { _parent = parent; @@ -52,22 +55,23 @@ namespace Ryujinx.UI.Widgets _virtualFileSystem = virtualFileSystem; _accountManager = accountManager; _horizonClient = horizonClient; - _applicationData = applicationData; + _titleFilePath = titleFilePath; + _titleName = titleName; + _titleIdText = titleId; + _controlData = controlData; - if (!_applicationData.ControlHolder.ByteSpan.IsZeros()) + if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId)) { - _openSaveUserDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.UserAccountSaveDataSize > 0; - _openSaveDeviceDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.DeviceSaveDataSize > 0; - _openSaveBcatDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - } - else - { - _openSaveUserDirMenuItem.Sensitive = false; - _openSaveDeviceDirMenuItem.Sensitive = false; - _openSaveBcatDirMenuItem.Sensitive = false; + GtkDialog.CreateErrorDialog("The selected game did not have a valid Title Id"); + + return; } - string fileExt = System.IO.Path.GetExtension(_applicationData.Path).ToLower(); + _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0; + _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0; + _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0; + + string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower(); bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; _extractRomFsMenuItem.Sensitive = hasNca; @@ -133,7 +137,7 @@ namespace Ryujinx.UI.Widgets private void OpenSaveDir(in SaveDataFilter saveDataFilter) { - if (!TryFindSaveData(_applicationData.Name, _applicationData.Id, _applicationData.ControlHolder, in saveDataFilter, out ulong saveDataId)) + if (!TryFindSaveData(_titleName, _titleId, _controlData, in saveDataFilter, out ulong saveDataId)) { return; } @@ -186,7 +190,7 @@ namespace Ryujinx.UI.Widgets { Title = "Ryujinx - NCA Section Extractor", Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_applicationData.Path)}...", + SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...", WindowPosition = WindowPosition.Center, }; @@ -198,16 +202,29 @@ namespace Ryujinx.UI.Widgets } }); - using FileStream file = new(_applicationData.Path, FileMode.Open, FileAccess.Read); + using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read); Nca mainNca = null; Nca patchNca = null; - if ((System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nsp") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".pfs0") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".xci")) + if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") || + (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") || + (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci")) { - IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(_applicationData.Path, _virtualFileSystem); + IFileSystem pfs; + + if (System.IO.Path.GetExtension(_titleFilePath) == ".xci") + { + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); + + pfs = xci.OpenPartition(XciPartitionType.Secure); + } + else + { + var pfsTemp = new PartitionFileSystem(); + pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure(); + pfs = pfsTemp; + } foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { @@ -232,7 +249,7 @@ namespace Ryujinx.UI.Widgets } } } - else if (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nca") + else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca") { mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); } @@ -249,11 +266,7 @@ namespace Ryujinx.UI.Widgets return; } - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); if (updatePatchNca != null) { @@ -447,44 +460,44 @@ namespace Ryujinx.UI.Widgets private void OpenSaveUserDir_Clicked(object sender, EventArgs args) { var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, saveType: default, userId, saveDataId: default, index: default); + var saveDataFilter = SaveDataFilter.Make(_titleId, saveType: default, userId, saveDataId: default, index: default); OpenSaveDir(in saveDataFilter); } private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args) { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default); + var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Device, userId: default, saveDataId: default, index: default); OpenSaveDir(in saveDataFilter); } private void OpenSaveBcatDir_Clicked(object sender, EventArgs args) { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default); + var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Bcat, userId: default, saveDataId: default, index: default); OpenSaveDir(in saveDataFilter); } private void ManageTitleUpdates_Clicked(object sender, EventArgs args) { - new TitleUpdateWindow(_parent, _virtualFileSystem, _applicationData).Show(); + new TitleUpdateWindow(_parent, _virtualFileSystem, _titleIdText, _titleName).Show(); } private void ManageDlc_Clicked(object sender, EventArgs args) { - new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show(); + new DlcWindow(_virtualFileSystem, _titleIdText, _titleName).Show(); } private void ManageCheats_Clicked(object sender, EventArgs args) { - new CheatWindow(_virtualFileSystem, _applicationData.Id, _applicationData.Name, _applicationData.Path).Show(); + new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show(); } private void OpenTitleModDir_Clicked(object sender, EventArgs args) { string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _applicationData.IdString); + string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } @@ -492,7 +505,7 @@ namespace Ryujinx.UI.Widgets private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _applicationData.IdString); + string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } @@ -514,7 +527,7 @@ namespace Ryujinx.UI.Widgets private void OpenPtcDir_Clicked(object sender, EventArgs args) { - string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu"); + string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu"); string mainPath = System.IO.Path.Combine(ptcDir, "0"); string backupPath = System.IO.Path.Combine(ptcDir, "1"); @@ -531,7 +544,7 @@ namespace Ryujinx.UI.Widgets private void OpenShaderCacheDir_Clicked(object sender, EventArgs args) { - string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader"); + string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"); if (!Directory.Exists(shaderCacheDir)) { @@ -543,10 +556,10 @@ namespace Ryujinx.UI.Widgets private void PurgePtcCache_Clicked(object sender, EventArgs args) { - DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "1")); + DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0")); + DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1")); - MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); + MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n{_titleName}\n\nAre you sure you want to proceed?"); List cacheFiles = new(); @@ -580,9 +593,9 @@ namespace Ryujinx.UI.Widgets private void PurgeShaderCache_Clicked(object sender, EventArgs args) { - DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader")); + DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader")); - using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); + using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n{_titleName}\n\nAre you sure you want to proceed?"); List oldCacheDirectories = new(); List newCacheFiles = new(); @@ -624,11 +637,8 @@ namespace Ryujinx.UI.Widgets private void CreateShortcut_Clicked(object sender, EventArgs args) { - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id); - ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon); + byte[] appIcon = new ApplicationLibrary(_virtualFileSystem).GetApplicationIcon(_titleFilePath, ConfigurationState.Instance.System.Language); + ShortcutHelper.CreateAppShortcut(_titleFilePath, _titleName, _titleIdText, appIcon); } } } diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs index fcd960df0..7cddc362b 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs @@ -9,13 +9,16 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Memory; using Ryujinx.HLE.FileSystem; using Ryujinx.UI.Common.Configuration; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; +using Image = SixLabors.ImageSharp.Image; namespace Ryujinx.UI.Windows { @@ -141,11 +144,9 @@ namespace Ryujinx.UI.Windows stream.Position = 0; - using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888)); - var data = DecompressYaz0(stream); - Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length); + Image avatarImage = Image.LoadPixelData(DecompressYaz0(stream), 256, 256); - avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80); + avatarImage.SaveAsPng(streamPng); _avatarDict.Add(item.FullPath, streamPng.ToArray()); } @@ -169,23 +170,15 @@ namespace Ryujinx.UI.Windows { using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - using var avatarImage = SKBitmap.Decode(data); - using var surface = SKSurface.Create(avatarImage.Info); + Image avatarImage = Image.Load(data, new PngDecoder()); - var background = new SKColor( + avatarImage.Mutate(x => x.BackgroundColor(new Rgba32( (byte)(_backgroundColor.Red * 255), (byte)(_backgroundColor.Green * 255), (byte)(_backgroundColor.Blue * 255), (byte)(_backgroundColor.Alpha * 255) - ); - 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); + ))); + avatarImage.SaveAsJpeg(streamJpg); return streamJpg.ToArray(); } @@ -240,7 +233,7 @@ namespace Ryujinx.UI.Windows reader.ReadInt64(); // Padding byte[] input = new byte[stream.Length - stream.Position]; - stream.ReadExactly(input, 0, input.Length); + stream.Read(input, 0, input.Length); long inputOffset = 0; diff --git a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs index d9f01918f..96ed0723e 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs @@ -1,9 +1,7 @@ using Gtk; -using LibHac.Tools.FsSystem; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.IO; @@ -29,13 +27,8 @@ namespace Ryujinx.UI.Windows private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow")) { builder.Autoconnect(this); - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath)}"; + _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}"; string modsBasePath = ModLoader.GetModsBasePath(); string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId.ToString("X16")); diff --git a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs index fb3189e1c..388f11089 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs @@ -2,21 +2,17 @@ using Gtk; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.FsSystem; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; using Ryujinx.UI.Widgets; using System; using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; using GUI = Gtk.Builder.ObjectAttribute; namespace Ryujinx.UI.Windows @@ -24,7 +20,7 @@ namespace Ryujinx.UI.Windows public class DlcWindow : Window { private readonly VirtualFileSystem _virtualFileSystem; - private readonly string _applicationIdBase; + private readonly string _titleId; private readonly string _dlcJsonPath; private readonly List _dlcContainerList; @@ -36,16 +32,16 @@ namespace Ryujinx.UI.Windows [GUI] TreeSelection _dlcTreeSelection; #pragma warning restore CS0649, IDE0044 - public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { } + public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { } - private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow")) + private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_dlcWindow")) { builder.Autoconnect(this); - _applicationIdBase = applicationIdBase; + _titleId = titleId; _virtualFileSystem = virtualFileSystem; - _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json"); - _baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]"; + _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); + _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]"; try { @@ -76,7 +72,7 @@ namespace Ryujinx.UI.Windows }; _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _dlcTreeView.AppendColumn("ApplicationId", new CellRendererText(), "text", 1); + _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); foreach (DownloadableContentContainer dlcContainer in _dlcContainerList) @@ -90,18 +86,18 @@ namespace Ryujinx.UI.Windows bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath); - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(dlcContainer.ContainerPath, _virtualFileSystem, false); + using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath); - if (partitionFileSystem == null) - { - continue; - } + PartitionFileSystem pfs = new(); + pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure(); + + _virtualFileSystem.ImportTickets(pfs); foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList) { using var ncaFile = new UniqueRef(); - partitionFileSystem.OpenFile(ref ncaFile.Ref, dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.ContainerPath); if (nca != null) @@ -116,9 +112,6 @@ namespace Ryujinx.UI.Windows TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.ContainerPath}"); } } - - // NOTE: Try to load downloadable contents from PFS last to preserve enabled state. - AddDlc(applicationData.Path, true); } private Nca TryCreateNca(IStorage ncaStorage, string containerPath) @@ -135,52 +128,6 @@ namespace Ryujinx.UI.Windows return null; } - private void AddDlc(string path, bool ignoreNotFound = false) - { - if (!File.Exists(path) || _dlcContainerList.Any(x => x.ContainerPath == path)) - { - return; - } - - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - bool containsDlc = false; - - TreeIter? parentIter = null; - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path); - - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber)) - { - continue; - } - - parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", path); - - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); - containsDlc = true; - } - } - - if (!containsDlc && !ignoreNotFound) - { - GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); - } - } - private void AddButton_Clicked(object sender, EventArgs args) { FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") @@ -200,7 +147,52 @@ namespace Ryujinx.UI.Windows { foreach (string containerPath in fileChooser.Filenames) { - AddDlc(containerPath); + if (!File.Exists(containerPath)) + { + return; + } + + using FileStream containerFile = File.OpenRead(containerPath); + + PartitionFileSystem pfs = new(); + pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure(); + bool containsDlc = false; + + _virtualFileSystem.ImportTickets(pfs); + + TreeIter? parentIter = null; + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); + + if (nca == null) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.PublicData) + { + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) + { + break; + } + + parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath); + + ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); + containsDlc = true; + } + } + + if (!containsDlc) + { + GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); + } } } diff --git a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs index a08f59597..74b2330ee 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs @@ -2,17 +2,14 @@ using Gtk; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.Ncm; +using LibHac.FsSystem; using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Widgets; using System; using System.Collections.Generic; @@ -27,7 +24,7 @@ namespace Ryujinx.UI.Windows { private readonly MainWindow _parent; private readonly VirtualFileSystem _virtualFileSystem; - private readonly ApplicationData _applicationData; + private readonly string _titleId; private readonly string _updateJsonPath; private TitleUpdateMetadata _titleUpdateWindowData; @@ -41,17 +38,17 @@ namespace Ryujinx.UI.Windows [GUI] RadioButton _noUpdateRadioButton; #pragma warning restore CS0649, IDE0044 - public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, applicationData) { } + public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Gtk3.UI.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { } - private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_titleUpdateWindow")) + private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_titleUpdateWindow")) { _parent = parent; builder.Autoconnect(this); - _applicationData = applicationData; + _titleId = titleId; _virtualFileSystem = virtualFileSystem; - _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json"); + _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json"); _radioButtonToPathDictionary = new Dictionary(); try @@ -67,10 +64,7 @@ namespace Ryujinx.UI.Windows }; } - _baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]"; - - // Try to get updates from PFS first - AddUpdate(_applicationData.Path, true); + _baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]"; foreach (string path in _titleUpdateWindowData.Paths) { @@ -90,68 +84,46 @@ namespace Ryujinx.UI.Windows } } - private void AddUpdate(string path, bool ignoreNotFound = false) + private void AddUpdate(string path) { - if (!File.Exists(path) || _radioButtonToPathDictionary.ContainsValue(path)) + if (File.Exists(path)) { - return; - } + using FileStream file = new(path, FileMode.Open, FileAccess.Read); - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; + PartitionFileSystem nsp = new(); + nsp.Initialize(file.AsStorage()).ThrowIfFailure(); - try - { - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - Dictionary updates = pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel); - - Nca patchNca = null; - Nca controlNca = null; - - if (updates.TryGetValue(_applicationData.Id, out ContentMetaData update)) + try { - patchNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Program); - controlNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Control); - } + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using var nacpFile = new UniqueRef(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - string radioLabel = $"Version {controlData.DisplayVersionString.ToString()} - {path}"; - - if (System.IO.Path.GetExtension(path).ToLower() == ".xci") + if (controlNca != null && patchNca != null) { - radioLabel = "Bundled: " + radioLabel; + ApplicationControlProperty controlData = new(); + + using var nacpFile = new UniqueRef(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + RadioButton radioButton = new($"Version {controlData.DisplayVersionString.ToString()} - {path}"); + radioButton.JoinGroup(_noUpdateRadioButton); + + _availableUpdatesBox.Add(radioButton); + _radioButtonToPathDictionary.Add(radioButton, path); + + radioButton.Show(); + radioButton.Active = true; } - - RadioButton radioButton = new(radioLabel); - radioButton.JoinGroup(_noUpdateRadioButton); - - _availableUpdatesBox.Add(radioButton); - _radioButtonToPathDictionary.Add(radioButton, path); - - radioButton.Show(); - radioButton.Active = true; - } - else - { - if (!ignoreNotFound) + else { GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); } } - } - catch (Exception exception) - { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); + catch (Exception exception) + { + GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); + } } } diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs index 77afc5d1f..d1e5fa9fc 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs @@ -4,13 +4,15 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Widgets; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; 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 { @@ -175,13 +177,13 @@ namespace Ryujinx.UI.Windows private void ProcessProfileImage(byte[] buffer) { - using var image = SKBitmap.Decode(buffer); + using Image image = Image.Load(buffer); - image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High); + image.Mutate(x => x.Resize(256, 256)); using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80); + image.SaveAsJpeg(streamJpg); _bufferImageProfile = streamJpg.ToArray(); } diff --git a/src/Ryujinx.HLE.Generators/CodeGenerator.cs b/src/Ryujinx.HLE.Generators/CodeGenerator.cs deleted file mode 100644 index 7e4848ad3..000000000 --- a/src/Ryujinx.HLE.Generators/CodeGenerator.cs +++ /dev/null @@ -1,63 +0,0 @@ -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(); - } - } -} diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs deleted file mode 100644 index 19fdbe197..000000000 --- a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs +++ /dev/null @@ -1,76 +0,0 @@ -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()); - } - } -} diff --git a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj deleted file mode 100644 index eeab9c0e9..000000000 --- a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.0 - true - true - Generated - true - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - diff --git a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs deleted file mode 100644 index e4269cb9a..000000000 --- a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Generic; - -namespace Ryujinx.HLE.Generators -{ - internal class ServiceSyntaxReceiver : ISyntaxReceiver - { - public HashSet Types = new HashSet(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is ClassDeclarationSyntax classDeclaration) - { - if (classDeclaration.BaseList == null) - { - return; - } - - Types.Add(classDeclaration); - } - } - } -} diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs index e6c0fce08..3c34a886b 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -14,7 +14,6 @@ using Ryujinx.Common.Utilities; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Services.Ssl; using Ryujinx.HLE.HOS.Services.Time; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -185,6 +184,41 @@ namespace Ryujinx.HLE.FileSystem } } + // fs must contain AOC nca files in its root + public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel) + { + _virtualFileSystem.ImportTickets(fs); + + foreach (var ncaPath in fs.EnumerateEntries("*.cnmt.nca", SearchOptions.Default)) + { + using var ncaFile = new UniqueRef(); + + fs.OpenFile(ref ncaFile.Ref, ncaPath.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + var nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + if (nca.Header.ContentType != NcaContentType.Meta) + { + Logger.Warning?.Print(LogClass.Application, $"{ncaPath} is not a valid metadata file"); + + continue; + } + + using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel); + using var cnmtFile = new UniqueRef(); + + pfs0.OpenFile(ref cnmtFile.Ref, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + var cnmt = new Cnmt(cnmtFile.Get.AsStream()); + if (cnmt.Type != ContentMetaType.AddOnContent || (cnmt.TitleId & 0xFFFFFFFFFFFFE000) != aocBaseId) + { + continue; + } + + string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower(); + + AddAocItem(cnmt.TitleId, containerPath, $"/{ncaId}.nca", true); + } + } + public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false) { // TODO: Check Aoc version. @@ -198,7 +232,11 @@ namespace Ryujinx.HLE.FileSystem if (!mergedToContainer) { - using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerPath, _virtualFileSystem); + using FileStream fileStream = File.OpenRead(containerPath); + using PartitionFileSystem partitionFileSystem = new(); + partitionFileSystem.Initialize(fileStream.AsStorage()).ThrowIfFailure(); + + _virtualFileSystem.ImportTickets(partitionFileSystem); } } } diff --git a/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs b/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs deleted file mode 100644 index aebcf7988..000000000 --- a/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs +++ /dev/null @@ -1,61 +0,0 @@ -using LibHac.Common.Keys; -using LibHac.Fs.Fsa; -using LibHac.Ncm; -using LibHac.Tools.FsSystem.NcaUtils; -using LibHac.Tools.Ncm; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using System; - -namespace Ryujinx.HLE.FileSystem -{ - /// - /// Thin wrapper around - /// - public class ContentMetaData - { - private readonly IFileSystem _pfs; - private readonly Cnmt _cnmt; - - public ulong Id => _cnmt.TitleId; - public TitleVersion Version => _cnmt.TitleVersion; - public ContentMetaType Type => _cnmt.Type; - public ulong ApplicationId => _cnmt.ApplicationTitleId; - public ulong PatchId => _cnmt.PatchTitleId; - public TitleVersion RequiredSystemVersion => _cnmt.MinimumSystemVersion; - public TitleVersion RequiredApplicationVersion => _cnmt.MinimumApplicationVersion; - public byte[] Digest => _cnmt.Hash; - - public ulong ProgramBaseId => Id & ~0x1FFFUL; - public bool IsSystemTitle => _cnmt.Type < ContentMetaType.Application; - - public ContentMetaData(IFileSystem pfs, Cnmt cnmt) - { - _pfs = pfs; - _cnmt = cnmt; - } - - public Nca GetNcaByType(KeySet keySet, ContentType type, int programIndex = 0) - { - // TODO: Replace this with a check for IdOffset as soon as LibHac supports it: - // && entry.IdOffset == programIndex - - foreach (var entry in _cnmt.ContentEntries) - { - if (entry.Type != type) - { - continue; - } - - string ncaId = BitConverter.ToString(entry.NcaId).Replace("-", null).ToLower(); - Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca"); - - if (nca.GetProgramIndex() == programIndex) - { - return nca; - } - } - - return null; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs index 3c34d5c78..30300f1b6 100644 --- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs +++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -8,24 +8,27 @@ namespace Ryujinx.HLE.HOS.Applets { static class AppletManager { + private static readonly Dictionary _appletMapping; + + static AppletManager() + { + _appletMapping = new Dictionary + { + { 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) { - switch (applet) + if (_appletMapping.TryGetValue(applet, out Type appletClass)) { - 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); + return (IApplet)Activator.CreateInstance(appletClass, system); } throw new NotImplementedException($"{applet} applet is not implemented."); diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs index 239535ad5..3f7516e6a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs @@ -112,16 +112,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { // Update the parameters that were provided. _state.InputText = inputText ?? _state.InputText; - _state.CursorBegin = Math.Max(0, cursorBegin.GetValueOrDefault(_state.CursorBegin)); - _state.CursorEnd = Math.Min(cursorEnd.GetValueOrDefault(_state.CursorEnd), _state.InputText.Length); + _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin); + _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd); _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; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index cc62eca1d..9e48568e1 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -1,9 +1,14 @@ using Ryujinx.HLE.UI; using Ryujinx.Memory; -using SkiaSharp; +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using System; using System.Diagnostics; using System.IO; +using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; @@ -24,39 +29,38 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private readonly object _bufferLock = new(); private RenderingSurfaceInfo _surfaceInfo = null; - private SKImageInfo _imageInfo; - private SKSurface _surface = null; + private Image _surface = null; private byte[] _bufferData = null; - private readonly SKBitmap _ryujinxLogo = null; - private readonly SKBitmap _padAcceptIcon = null; - private readonly SKBitmap _padCancelIcon = null; - private readonly SKBitmap _keyModeIcon = null; + private readonly Image _ryujinxLogo = null; + private readonly Image _padAcceptIcon = null; + private readonly Image _padCancelIcon = null; + private readonly Image _keyModeIcon = null; private readonly float _textBoxOutlineWidth; private readonly float _padPressedPenWidth; - private readonly SKColor _textNormalColor; - private readonly SKColor _textSelectedColor; - private readonly SKColor _textOverCursorColor; + private readonly Color _textNormalColor; + private readonly Color _textSelectedColor; + private readonly Color _textOverCursorColor; - private readonly SKPaint _panelBrush; - private readonly SKPaint _disabledBrush; - private readonly SKPaint _cursorBrush; - private readonly SKPaint _selectionBoxBrush; + private readonly Brush _panelBrush; + private readonly Brush _disabledBrush; + private readonly Brush _cursorBrush; + private readonly Brush _selectionBoxBrush; - private readonly SKPaint _textBoxOutlinePen; - private readonly SKPaint _cursorPen; - private readonly SKPaint _selectionBoxPen; - private readonly SKPaint _padPressedPen; + private readonly Pen _textBoxOutlinePen; + private readonly Pen _cursorPen; + private readonly Pen _selectionBoxPen; + private readonly Pen _padPressedPen; private readonly int _inputTextFontSize; - private SKFont _messageFont; - private SKFont _inputTextFont; - private SKFont _labelsTextFont; + private Font _messageFont; + private Font _inputTextFont; + private Font _labelsTextFont; - private SKRect _panelRectangle; - private SKPoint _logoPosition; + private RectangleF _panelRectangle; + private Point _logoPosition; private float _messagePositionY; public SoftwareKeyboardRendererBase(IHostUITheme uiTheme) @@ -74,10 +78,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard _padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0); _keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0); - var panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); - var panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); - var borderColor = ToColor(uiTheme.DefaultBorderColor); - var selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor); + Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); + Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); + Color borderColor = ToColor(uiTheme.DefaultBorderColor); + Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor); _textNormalColor = ToColor(uiTheme.DefaultForegroundColor); _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor); @@ -88,29 +92,15 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard _textBoxOutlineWidth = 2; _padPressedPenWidth = 2; - _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 }; + _panelBrush = new SolidBrush(panelColor); + _disabledBrush = new SolidBrush(panelTransparentColor); + _cursorBrush = new SolidBrush(_textNormalColor); + _selectionBoxBrush = new SolidBrush(selectionBackgroundColor); - _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 }; + _textBoxOutlinePen = Pens.Solid(borderColor, _textBoxOutlineWidth); + _cursorPen = Pens.Solid(_textNormalColor, cursorWidth); + _selectionBoxPen = Pens.Solid(selectionBackgroundColor, cursorWidth); + _padPressedPen = Pens.Solid(borderColor, _padPressedPenWidth); _inputTextFontSize = 20; @@ -133,10 +123,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { try { - using var typeface = SKTypeface.FromFamilyName(fontFamily, SKFontStyle.Normal); - _messageFont = new SKFont(typeface, 26); - _inputTextFont = new SKFont(typeface, _inputTextFontSize); - _labelsTextFont = new SKFont(typeface, 24); + _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular); + _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular); + _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular); return; } @@ -148,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!"); } - private static SKColor ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) + private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) { var a = (byte)(color.A * 255); var r = (byte)(color.R * 255); @@ -162,33 +151,34 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard b = (byte)(255 - b); } - return new SKColor(r, g, b, overrideAlpha.GetValueOrDefault(a)); + return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a)); } - private static SKBitmap LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) + private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) { Stream resourceStream = assembly.GetManifestResourceStream(resourcePath); return LoadResource(resourceStream, newWidth, newHeight); } - private static SKBitmap LoadResource(Stream resourceStream, int newWidth, int newHeight) + private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight) { Debug.Assert(resourceStream != null); - var bitmap = SKBitmap.Decode(resourceStream); + var image = Image.Load(resourceStream); if (newHeight != 0 && newWidth != 0) { - var resized = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.High); - if (resized != null) - { - bitmap.Dispose(); - bitmap = resized; - } + image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3)); } - return bitmap; + return image; + } + + private static void SetGraphicsOptions(IImageProcessingContext context) + { + context.GetGraphicsOptions().Antialias = true; + context.GetDrawingOptions().GraphicsOptions.Antialias = true; } private void DrawImmutableElements() @@ -197,18 +187,22 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { return; } - var canvas = _surface.Canvas; - canvas.Clear(SKColors.Transparent); - canvas.DrawRect(_panelRectangle, _panelBrush); - canvas.DrawBitmap(_ryujinxLogo, _logoPosition); + _surface.Mutate(context => + { + SetGraphicsOptions(context); - float halfWidth = _panelRectangle.Width / 2; - float buttonsY = _panelRectangle.Top + 185; + context.Clear(Color.Transparent); + context.Fill(_panelBrush, _panelRectangle); + context.DrawImage(_ryujinxLogo, _logoPosition, 1); - SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY); + float halfWidth = _panelRectangle.Width / 2; + float buttonsY = _panelRectangle.Y + 185; - DrawControllerToggle(canvas, disableButtonPosition); + PointF disableButtonPosition = new(halfWidth + 180, buttonsY); + + DrawControllerToggle(context, disableButtonPosition); + }); } public void DrawMutableElements(SoftwareKeyboardUIState state) @@ -218,43 +212,40 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard return; } - using var paint = new SKPaint(_messageFont) + _surface.Mutate(context => { - Color = _textNormalColor, - IsAntialias = true - }; + 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); - 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); + SetGraphicsOptions(context); - canvas.DrawRect(messageBoundRectangle, _panelBrush); + context.Fill(_panelBrush, messageBoundRectangle); - canvas.DrawText(MessageText, messagePosition.X, messagePosition.Y + _messageFont.Metrics.XHeight + _messageFont.Metrics.Descent, paint); + context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition); - 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. - canvas.DrawRect(messageBoundRectangle, _disabledBrush); - } + context.Fill(_disabledBrush, messageBoundRectangle); + } - DrawTextBox(canvas, state); + DrawTextBox(context, state); - float halfWidth = _panelRectangle.Width / 2; - float buttonsY = _panelRectangle.Top + 185; + float halfWidth = _panelRectangle.Width / 2; + float buttonsY = _panelRectangle.Y + 185; - 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); + PointF acceptButtonPosition = new(halfWidth - 180, buttonsY); + PointF cancelButtonPosition = new(halfWidth, buttonsY); + PointF disableButtonPosition = new(halfWidth + 180, buttonsY); + DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled); + DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled); + }); } public void CreateSurface(RenderingSurfaceInfo surfaceInfo) @@ -277,8 +268,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard Debug.Assert(_surfaceInfo.Height <= totalHeight); Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size); - _imageInfo = new SKImageInfo((int)totalWidth, (int)totalHeight, SKColorType.Rgba8888); - _surface = SKSurface.Create(_imageInfo); + _surface = new Image((int)totalWidth, (int)totalHeight); ComputeConstants(); DrawImmutableElements(); @@ -292,81 +282,76 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard int panelHeight = 240; int panelPositionY = totalHeight - panelHeight; - _panelRectangle = SKRect.Create(0, panelPositionY, totalWidth, panelHeight); + _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight); _messagePositionY = panelPositionY + 60; int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2; int logoPositionY = panelPositionY + 18; - _logoPosition = new SKPoint(logoPositionX, logoPositionY); + _logoPosition = new Point(logoPositionX, logoPositionY); } - private static SKRect MeasureString(string text, SKPaint paint) + private static RectangleF MeasureString(string text, Font font) { - SKRect bounds = SKRect.Empty; + TextOptions options = new(font); if (text == "") { - paint.MeasureText(" ", ref bounds); - } - else - { - paint.MeasureText(text, ref bounds); + FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options); + + return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); } - return bounds; + FontRectangle rectangle = TextMeasurer.MeasureSize(text, options); + + return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } - private static SKRect MeasureString(ReadOnlySpan text, SKPaint paint) + private static RectangleF MeasureString(ReadOnlySpan text, Font font) { - SKRect bounds = SKRect.Empty; + TextOptions options = new(font); if (text == "") { - paint.MeasureText(" ", ref bounds); - } - else - { - paint.MeasureText(text, ref bounds); + FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options); + return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); } - return bounds; + FontRectangle rectangle = TextMeasurer.MeasureSize(text, options); + + return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } - private void DrawTextBox(SKCanvas canvas, SoftwareKeyboardUIState state) + private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUIState state) { - using var textPaint = new SKPaint(_labelsTextFont) - { - IsAntialias = true, - Color = _textNormalColor - }; - var inputTextRectangle = MeasureString(state.InputText, textPaint); + var inputTextRectangle = MeasureString(state.InputText, _inputTextFont); - float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.Left + 8)); + float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8)); float boxHeight = 32; - float boxY = _panelRectangle.Top + 110; + float boxY = _panelRectangle.Y + 110; float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); - SKRect boxRectangle = SKRect.Create(boxX, boxY, boxWidth, boxHeight); + RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight); - SKRect boundRectangle = SKRect.Create(_panelRectangle.Left, boxY - _textBoxOutlineWidth, + RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth, _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth); - canvas.DrawRect(boundRectangle, _panelBrush); + context.Fill(_panelBrush, boundRectangle); - canvas.DrawRect(boxRectangle, _textBoxOutlinePen); + context.Draw(_textBoxOutlinePen, boxRectangle); - float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.Left; + float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X; float inputTextY = boxY + 5; - var inputTextPosition = new SKPoint(inputTextX, inputTextY); - canvas.DrawText(state.InputText, inputTextPosition.X, inputTextPosition.Y + (_labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent), textPaint); + var inputTextPosition = new PointF(inputTextX, inputTextY); + + context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition); // Draw the cursor on top of the text and redraw the text with a different color if necessary. - SKColor cursorTextColor; - SKPaint cursorBrush; - SKPaint cursorPen; + Color cursorTextColor; + Brush cursorBrush; + Pen cursorPen; float cursorPositionYTop = inputTextY + 1; float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1; @@ -386,12 +371,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard ReadOnlySpan textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin); ReadOnlySpan textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); - var selectionBeginRectangle = MeasureString(textUntilBegin, textPaint); - var selectionEndRectangle = MeasureString(textUntilEnd, textPaint); + var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont); + var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont); cursorVisible = true; - cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.Left; - cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.Left; + cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X; + cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X; } else { @@ -405,10 +390,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); ReadOnlySpan textUntilCursor = state.InputText.AsSpan(0, cursorBegin); - var cursorTextRectangle = MeasureString(textUntilCursor, textPaint); + var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorVisible = true; - cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left; + cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; if (state.OverwriteMode) { @@ -417,8 +402,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard if (state.CursorBegin < state.InputText.Length) { textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); - cursorTextRectangle = MeasureString(textUntilCursor, textPaint); - cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left; + cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); + cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; } else { @@ -445,32 +430,29 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard if (cursorWidth == 0) { - canvas.DrawLine(new SKPoint(cursorPositionXLeft, cursorPositionYTop), - new SKPoint(cursorPositionXLeft, cursorPositionYBottom), - cursorPen); + PointF[] points = { + new PointF(cursorPositionXLeft, cursorPositionYTop), + new PointF(cursorPositionXLeft, cursorPositionYBottom), + }; + + context.DrawLine(cursorPen, points); } else { - var cursorRectangle = SKRect.Create(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); + var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); - canvas.DrawRect(cursorRectangle, cursorPen); - canvas.DrawRect(cursorRectangle, cursorBrush); + context.Draw(cursorPen, cursorRectangle); + context.Fill(cursorBrush, cursorRectangle); - 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) + Image textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height); + textOverCursor.Mutate(context => { - Color = cursorTextColor, - IsAntialias = true - }; + var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y); + context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition); + }); - 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); + var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y); + context.DrawImage(textOverCursor, cursorPosition, 1); } } else if (!state.TypingEnabled) @@ -478,11 +460,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. - canvas.DrawRect(boundRectangle, _disabledBrush); + context.Fill(_disabledBrush, boundRectangle); } } - private void DrawPadButton(SKCanvas canvas, SKPoint point, SKBitmap icon, string label, bool pressed, bool enabled) + private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled) { // Use relative positions so we can center the entire drawing later. @@ -491,18 +473,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard float iconWidth = icon.Width; float iconHeight = icon.Height; - using var paint = new SKPaint(_labelsTextFont) - { - Color = _textNormalColor, - IsAntialias = true - }; + var labelRectangle = MeasureString(label, _labelsTextFont); - var labelRectangle = MeasureString(label, paint); - - float labelPositionX = iconWidth + 8 - labelRectangle.Left; + float labelPositionX = iconWidth + 8 - labelRectangle.X; float labelPositionY = 3; - float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.Left; + float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X; float fullHeight = iconHeight; // Convert all relative positions into absolute. @@ -513,24 +489,24 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard iconX += originX; iconY += originY; - var iconPosition = new SKPoint((int)iconX, (int)iconY); - var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY); + var iconPosition = new Point((int)iconX, (int)iconY); + var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); - var selectedRectangle = SKRect.Create(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth, + var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth, fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth); - var boundRectangle = SKRect.Create(originX, originY, fullWidth, fullHeight); + var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight); boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth); - canvas.DrawRect(boundRectangle, _panelBrush); - canvas.DrawBitmap(icon, iconPosition); - canvas.DrawText(label, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent, paint); + context.Fill(_panelBrush, boundRectangle); + context.DrawImage(icon, iconPosition, 1); + context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition); if (enabled) { if (pressed) { - canvas.DrawRect(selectedRectangle, _padPressedPen); + context.Draw(_padPressedPen, selectedRectangle); } } else @@ -538,26 +514,21 @@ 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. - canvas.DrawRect(boundRectangle, _disabledBrush); + context.Fill(_disabledBrush, boundRectangle); } } - private void DrawControllerToggle(SKCanvas canvas, SKPoint point) + private void DrawControllerToggle(IImageProcessingContext context, PointF point) { - using var paint = new SKPaint(_labelsTextFont) - { - IsAntialias = true, - Color = _textNormalColor - }; - var labelRectangle = MeasureString(ControllerToggleText, paint); + var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont); // Use relative positions so we can center the entire drawing later. float keyWidth = _keyModeIcon.Width; float keyHeight = _keyModeIcon.Height; - float labelPositionX = keyWidth + 8 - labelRectangle.Left; - float labelPositionY = -labelRectangle.Top - 1; + float labelPositionX = keyWidth + 8 - labelRectangle.X; + float labelPositionY = -labelRectangle.Y - 1; float keyX = 0; float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2); @@ -573,14 +544,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard keyX += originX; keyY += originY; - var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY); - var overlayPosition = new SKPoint((int)keyX, (int)keyY); + var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); + var overlayPosition = new Point((int)keyX, (int)keyY); - canvas.DrawBitmap(_keyModeIcon, overlayPosition); - canvas.DrawText(ControllerToggleText, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight, paint); + context.DrawImage(_keyModeIcon, overlayPosition, 1); + context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition); } - public unsafe void CopyImageToBuffer() + public void CopyImageToBuffer() { lock (_bufferLock) { @@ -590,20 +561,21 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard } // Convert the pixel format used in the image to the one used in the Switch surface. - _surface.Flush(); - var buffer = new byte[_imageInfo.BytesSize]; - fixed (byte* bufferPtr = buffer) + if (!_surface.DangerousTryGetSinglePixelMemory(out Memory pixels)) { - if (!_surface.ReadPixels(_imageInfo, (nint)bufferPtr, _imageInfo.RowBytes, 0, 0)) - { - return; - } + return; } - _bufferData = buffer; + _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray(); + Span dataConvert = MemoryMarshal.Cast(_bufferData); - Debug.Assert(buffer.Length == _surfaceInfo.Size); + Debug.Assert(_bufferData.Length == _surfaceInfo.Size); + + for (int i = 0; i < dataConvert.Length; i++) + { + dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8); + } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs index 3f194e0ed..10f0b6f78 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs @@ -28,8 +28,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange4GiBSystemDev or MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB, MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, - MemoryArrange.MemoryArrange6GiB => 4916 * MiB, - MemoryArrange.MemoryArrange8GiB => 6964 * MiB, + MemoryArrange.MemoryArrange6GiB or + MemoryArrange.MemoryArrange8GiB => 4916 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } @@ -42,8 +42,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB, MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB, MemoryArrange.MemoryArrange6GiB => 562 * MiB, - MemoryArrange.MemoryArrange6GiBAppletDev => 2193 * MiB, - MemoryArrange.MemoryArrange8GiB => 562 * MiB, + MemoryArrange.MemoryArrange6GiBAppletDev or + MemoryArrange.MemoryArrange8GiB => 2193 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs new file mode 100644 index 000000000..8dfa4303f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel.Memory +{ + enum AddressSpaceType + { + Addr32Bits = 0, + Addr36Bits = 1, + Addr32BitsNoMap = 2, + Addr39Bits = 3, + } +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index bf2bbb97b..58bbc0dbf 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -58,10 +58,11 @@ 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; @@ -97,21 +98,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _reservedAddressSpaceSize = reservedAddressSpaceSize; } + private static readonly int[] _addrSpaceSizes = { 32, 36, 32, 39 }; + public Result InitializeForProcess( - ProcessCreationFlags flags, + AddressSpaceType addrSpaceType, + bool aslrEnabled, 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 << GetAddressSpaceWidth(flags); + ulong addrSpaceSize = 1UL << _addrSpaceSizes[(int)addrSpaceType]; Result result = CreateUserAddressSpace( - flags, + addrSpaceType, + aslrEnabled, fromBack, addrSpaceBase, addrSpaceSize, @@ -128,22 +138,6 @@ 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; @@ -153,7 +147,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } private Result CreateUserAddressSpace( - ProcessCreationFlags flags, + AddressSpaceType addrSpaceType, + bool aslrEnabled, bool fromBack, ulong addrSpaceStart, ulong addrSpaceEnd, @@ -173,11 +168,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong stackAndTlsIoStart; ulong stackAndTlsIoEnd; - AliasRegionExtraSize = 0; - - switch (flags & ProcessCreationFlags.AddressSpaceMask) + switch (addrSpaceType) { - case ProcessCreationFlags.AddressSpace32Bit: + case AddressSpaceType.Addr32Bits: aliasRegion.Size = 0x40000000; heapRegion.Size = 0x40000000; stackRegion.Size = 0; @@ -190,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory stackAndTlsIoEnd = 0x40000000; break; - case ProcessCreationFlags.AddressSpace64BitDeprecated: + case AddressSpaceType.Addr36Bits: aliasRegion.Size = 0x180000000; heapRegion.Size = 0x180000000; stackRegion.Size = 0; @@ -203,7 +196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory stackAndTlsIoEnd = 0x80000000; break; - case ProcessCreationFlags.AddressSpace32BitWithoutAlias: + case AddressSpaceType.Addr32BitsNoMap: aliasRegion.Size = 0; heapRegion.Size = 0x80000000; stackRegion.Size = 0; @@ -216,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory stackAndTlsIoEnd = 0x40000000; break; - case ProcessCreationFlags.AddressSpace64Bit: + case AddressSpaceType.Addr39Bits: if (_reservedAddressSpaceSize < addrSpaceEnd) { int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize); @@ -225,8 +218,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory heapRegion.Size = 0x180000000; stackRegion.Size = 1UL << (addressSpaceWidth - 8); tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3); - CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment); - codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart; + CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment); + codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart; stackAndTlsIoStart = 0; stackAndTlsIoEnd = 0; AslrRegionStart = 0x8000000; @@ -246,16 +239,9 @@ 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($"Invalid process flags {flags}", nameof(flags)); + throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType)); } CodeRegionEnd = CodeRegionStart + codeRegionSize; @@ -280,8 +266,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong aslrMaxOffset = mapAvailableSize - mapTotalSize; - bool aslrEnabled = flags.HasFlag(ProcessCreationFlags.EnableAslr); - _aslrEnabled = aslrEnabled; AddrSpaceStart = addrSpaceStart; @@ -741,7 +725,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { address = 0; - if (size > HeapRegionEnd - HeapRegionStart || size > _heapCapacity) + if (size > HeapRegionEnd - HeapRegionStart) { return KernelResult.OutOfMemory; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 422f03c64..6008548be 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -126,6 +126,8 @@ 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) @@ -135,6 +137,8 @@ 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; @@ -144,8 +148,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process : KernelContext.SmallMemoryBlockSlabManager; Result result = MemoryManager.InitializeForProcess( - creationInfo.Flags, - !creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr), + addrSpaceType, + aslrEnabled, + !aslrEnabled, memRegion, codeAddress, codeSize, @@ -229,6 +234,8 @@ 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) @@ -238,13 +245,16 @@ 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( - creationInfo.Flags, - !creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr), + addrSpaceType, + aslrEnabled, + !aslrEnabled, memRegion, codeAddress, codeSize, @@ -299,8 +309,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 = Capabilities.KernelReleaseVersion >> 19; - uint requiredKernelVersionMinor = (Capabilities.KernelReleaseVersion >> 15) & 0xf; + uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; + uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf; if (KernelContext.EnableVersionChecks) { @@ -509,10 +519,12 @@ 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) { @@ -1170,10 +1182,5 @@ namespace Ryujinx.HLE.HOS.Kernel.Process // TODO return false; } - - public bool IsSvcPermitted(int svcId) - { - return Capabilities.IsSvcPermitted(svcId); - } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index ebab67bb8..314aadf36 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -8,8 +8,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KProcessCapabilities { - private const int SvcMaskElementBits = 8; - public byte[] SvcAccessMask { get; } public byte[] IrqAccessMask { get; } @@ -24,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KProcessCapabilities() { // length / number of bits of the underlying type - SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / SvcMaskElementBits]; + SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8]; IrqAccessMask = new byte[0x80]; } @@ -210,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.MaximumExceeded; } - SvcAccessMask[svcId / SvcMaskElementBits] |= (byte)(1 << (svcId % SvcMaskElementBits)); + SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7)); } break; @@ -326,13 +324,5 @@ 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; - } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs index 1b62a29d4..f0e43e023 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs @@ -29,8 +29,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process PoolPartitionMask = 0xf << PoolPartitionShift, OptimizeMemoryAllocation = 1 << 11, - DisableDeviceAddressSpaceMerge = 1 << 12, - EnableAliasRegionExtraSize = 1 << 13, All = Is64Bit | @@ -40,8 +38,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process IsApplication | DeprecatedUseSecureMemory | PoolPartitionMask | - OptimizeMemoryAllocation | - DisableDeviceAddressSpaceMerge | - EnableAliasRegionExtraSize, + OptimizeMemoryAllocation, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs index cbaae8780..c0db82105 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs @@ -21,17 +21,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall SystemResourceSizeTotal, SystemResourceSizeUsed, ProgramId, - InitialProcessIdRange, // NOTE: Added in 4.0.0, removed in 5.0.0. + // NOTE: Added in 4.0.0, removed in 5.0.0. + InitialProcessIdRange, UserExceptionContextAddress, TotalNonSystemMemorySize, UsedNonSystemMemorySize, IsApplication, FreeThreadCount, ThreadTickCount, - IsSvcPermitted, - IoRegionHint, - AliasRegionExtraSize, - MesosphereCurrentProcess = 65001, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 2f487243d..6595ecef2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -84,17 +84,6 @@ 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)) { @@ -150,6 +139,7 @@ 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(handle); @@ -182,14 +172,17 @@ 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 @@ -263,6 +256,7 @@ 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, @@ -312,6 +306,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 [Svc(0x23)] public Result SendAsyncRequestWithUserBuffer( @@ -621,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } - ArrayPool.Shared.Return(syncObjsArray, true); + ArrayPool.Shared.Return(syncObjsArray); return result; } @@ -901,6 +896,7 @@ 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)) @@ -932,8 +928,10 @@ 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, @@ -981,8 +979,10 @@ 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,8 +1018,10 @@ 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)) @@ -1055,6 +1057,7 @@ 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) @@ -1071,6 +1074,7 @@ 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(); @@ -1090,8 +1094,10 @@ 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)) @@ -1137,8 +1143,10 @@ 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)) @@ -1178,6 +1186,7 @@ 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) @@ -1244,6 +1253,7 @@ 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)) @@ -1289,8 +1299,10 @@ 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)) @@ -1330,8 +1342,10 @@ 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)) @@ -1366,8 +1380,10 @@ 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)) @@ -1402,6 +1418,7 @@ 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) @@ -1445,6 +1462,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x4c)] +#pragma warning disable CA1822 // Mark member as static public Result ControlCodeMemory( int handle, CodeMemoryOperation op, @@ -1522,12 +1540,14 @@ 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, - ulong size, + [PointerSized] ulong src, + [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(src)) @@ -1564,8 +1584,10 @@ 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, @@ -1621,8 +1643,10 @@ 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, @@ -1667,8 +1691,10 @@ 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)) @@ -1705,8 +1731,10 @@ 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)) @@ -1743,6 +1771,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); } +#pragma warning restore CA1822 private static bool PageAligned(ulong address) { @@ -1752,6 +1781,7 @@ 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(); @@ -1780,12 +1810,15 @@ 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) @@ -1878,6 +1911,7 @@ 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(); @@ -1903,8 +1937,10 @@ 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(); @@ -1913,6 +1949,7 @@ 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) @@ -1941,7 +1978,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall case InfoType.UsedNonSystemMemorySize: case InfoType.IsApplication: case InfoType.FreeThreadCount: - case InfoType.AliasRegionExtraSize: { if (subId != 0) { @@ -1970,19 +2006,22 @@ 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; @@ -1990,6 +2029,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall case InfoType.AslrRegionAddress: value = process.MemoryManager.GetAddrSpaceBaseAddr(); break; + case InfoType.AslrRegionSize: value = process.MemoryManager.GetAddrSpaceSize(); break; @@ -1998,17 +2038,20 @@ 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: @@ -2022,6 +2065,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall case InfoType.TotalNonSystemMemorySize: value = process.GetMemoryCapacityWithoutPersonalMmHeap(); break; + case InfoType.UsedNonSystemMemorySize: value = process.GetMemoryUsageWithoutPersonalMmHeap(); break; @@ -2040,12 +2084,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { value = 0; } - break; - case InfoType.AliasRegionExtraSize: - value = process.MemoryManager.AliasRegionExtraSize; break; } + break; } @@ -2062,6 +2104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } value = KernelStatic.GetCurrentProcess().Debug ? 1UL : 0UL; + break; } @@ -2093,6 +2136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall value = (uint)resLimHandle; } + break; } @@ -2111,6 +2155,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } value = (ulong)KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks); + break; } @@ -2129,6 +2174,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); value = currentProcess.RandomEntropy[subId]; + break; } @@ -2174,22 +2220,7 @@ 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; } @@ -2200,7 +2231,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } - if (subId != 0) + if ((ulong)subId != 0) { return KernelResult.InvalidCombination; } @@ -2215,7 +2246,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } - value = (uint)outHandle; + value = (ulong)outHandle; + break; } @@ -2366,6 +2398,7 @@ 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; @@ -2386,8 +2419,10 @@ 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; @@ -2408,8 +2443,10 @@ 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; @@ -2430,6 +2467,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x7d)] public Result CreateResourceLimit(out int handle) @@ -2442,6 +2480,7 @@ 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) @@ -2458,6 +2497,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return resourceLimit.SetLimitValue(resource, limitValue); } +#pragma warning restore CA1822 // Thread @@ -2537,6 +2577,7 @@ 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(); @@ -2563,14 +2604,17 @@ 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) @@ -2597,6 +2641,7 @@ 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(); @@ -2616,8 +2661,10 @@ 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. @@ -2635,8 +2682,10 @@ 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(); @@ -2658,8 +2707,10 @@ 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(); @@ -2707,14 +2758,18 @@ 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(); @@ -2734,8 +2789,10 @@ 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(); @@ -2759,8 +2816,10 @@ 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(); @@ -2794,6 +2853,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 // Thread synchronization @@ -2926,6 +2986,7 @@ 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)) @@ -2942,8 +3003,10 @@ 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)) @@ -2960,8 +3023,10 @@ 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, @@ -2991,8 +3056,10 @@ 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(); @@ -3001,8 +3068,10 @@ 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)) @@ -3033,8 +3102,10 @@ 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)) @@ -3060,14 +3131,17 @@ 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. diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 8ef77902c..905c61d66 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -28,25 +28,42 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private SchedulingState _state; + private AutoResetEvent _idleInterruptEvent; + private readonly object _idleInterruptEventLock; + private KThread _previousThread; private KThread _currentThread; - - private int _coreIdleLock; - private bool _idleSignalled = true; - private bool _idleActive = true; - private long _idleTimeRunning; + private readonly KThread _idleThread; public KThread PreviousThread => _previousThread; public KThread CurrentThread => _currentThread; public long LastContextSwitchTime { get; private set; } - public long TotalIdleTimeTicks => _idleTimeRunning; + public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning; public KScheduler(KernelContext context, int coreId) { _context = context; _coreId = coreId; - _currentThread = null; + _idleInterruptEvent = new AutoResetEvent(false); + _idleInterruptEventLock = new object(); + + KThread idleThread = CreateIdleThread(context, coreId); + + _currentThread = idleThread; + _idleThread = idleThread; + + idleThread.StartHostThread(); + idleThread.SchedulerWaitEvent.Set(); + } + + private KThread CreateIdleThread(KernelContext context, int cpuCore) + { + KThread idleThread = new(context); + + idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop); + + return idleThread; } public static ulong SelectThreads(KernelContext context) @@ -220,64 +237,39 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread; // Request the thread running on that core to stop and reschedule, if we have one. - threadToSignal?.Context.RequestInterrupt(); + if (threadToSignal != context.Schedulers[coreToSignal]._idleThread) + { + threadToSignal.Context.RequestInterrupt(); + } // If the core is idle, ensure that the idle thread is awaken. - context.Schedulers[coreToSignal].NotifyIdleThread(); + context.Schedulers[coreToSignal]._idleInterruptEvent.Set(); scheduledCoresMask &= ~(1UL << coreToSignal); } } - private void ActivateIdleThread() + private void IdleThreadLoop() { - while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0) - { - Thread.SpinWait(1); - } - - Thread.MemoryBarrier(); - - // Signals that idle thread is now active on this core. - _idleActive = true; - - TryLeaveIdle(); - - Interlocked.Exchange(ref _coreIdleLock, 0); - } - - private void NotifyIdleThread() - { - while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0) - { - Thread.SpinWait(1); - } - - Thread.MemoryBarrier(); - - // Signals that the idle core may be able to exit idle. - _idleSignalled = true; - - TryLeaveIdle(); - - Interlocked.Exchange(ref _coreIdleLock, 0); - } - - public void TryLeaveIdle() - { - if (_idleSignalled && _idleActive) + while (_context.Running) { _state.NeedsScheduling = false; Thread.MemoryBarrier(); - KThread nextThread = PickNextThread(null, _state.SelectedThread); + KThread nextThread = PickNextThread(_state.SelectedThread); - if (nextThread != null) + if (_idleThread != nextThread) { - _idleActive = false; - nextThread.SchedulerWaitEvent.Set(); + _idleThread.SchedulerWaitEvent.Reset(); + WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent); } - _idleSignalled = false; + _idleInterruptEvent.WaitOne(); + } + + lock (_idleInterruptEventLock) + { + _idleInterruptEvent.Dispose(); + _idleInterruptEvent = null; } } @@ -300,37 +292,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // Wake all the threads that might be waiting until this thread context is unlocked. for (int core = 0; core < CpuCoresCount; core++) { - _context.Schedulers[core].NotifyIdleThread(); + _context.Schedulers[core]._idleInterruptEvent.Set(); } - KThread nextThread = PickNextThread(KernelStatic.GetCurrentThread(), selectedThread); + KThread nextThread = PickNextThread(selectedThread); if (currentThread.Context.Running) { // Wait until this thread is scheduled again, and allow the next thread to run. - - if (nextThread == null) - { - ActivateIdleThread(); - currentThread.SchedulerWaitEvent.WaitOne(); - } - else - { - WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent); - } + WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent); } else { // Allow the next thread to run. - - if (nextThread == null) - { - ActivateIdleThread(); - } - else - { - nextThread.SchedulerWaitEvent.Set(); - } + nextThread.SchedulerWaitEvent.Set(); // We don't need to wait since the thread is exiting, however we need to // make sure this thread will never call the scheduler again, since it is @@ -344,7 +319,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - private KThread PickNextThread(KThread currentThread, KThread selectedThread) + private KThread PickNextThread(KThread selectedThread) { while (true) { @@ -360,7 +335,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // on the core, as the scheduled thread will handle the next switch. if (selectedThread.ThreadContext.Lock()) { - SwitchTo(currentThread, selectedThread); + SwitchTo(selectedThread); if (!_state.NeedsScheduling) { @@ -371,15 +346,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - return null; + return _idleThread; } } else { // The core is idle now, make sure that the idle thread can run // and switch the core when a thread is available. - SwitchTo(currentThread, null); - return null; + SwitchTo(null); + return _idleThread; } _state.NeedsScheduling = false; @@ -388,9 +363,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - private void SwitchTo(KThread currentThread, KThread nextThread) + private void SwitchTo(KThread nextThread) { - KProcess currentProcess = currentThread?.Owner; + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + KThread currentThread = KernelStatic.GetCurrentThread(); + + nextThread ??= _idleThread; if (currentThread != nextThread) { @@ -398,14 +376,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading long currentTicks = PerformanceCounter.ElapsedTicks; long ticksDelta = currentTicks - previousTicks; - if (currentThread == null) - { - Interlocked.Add(ref _idleTimeRunning, ticksDelta); - } - else - { - currentThread.AddCpuTime(ticksDelta); - } + currentThread.AddCpuTime(ticksDelta); currentProcess?.AddCpuTime(ticksDelta); @@ -415,13 +386,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null; } - else if (currentThread == null) + else if (currentThread == _idleThread) { _previousThread = null; } } - if (nextThread != null && nextThread.CurrentCore != _coreId) + if (nextThread.CurrentCore != _coreId) { nextThread.CurrentCore = _coreId; } @@ -674,7 +645,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public void Dispose() { - // No resources to dispose for now. + // Ensure that the idle thread is not blocked and can exit. + lock (_idleInterruptEventLock) + { + _idleInterruptEvent?.Set(); + } } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index 21c2730bf..b1af06b0d 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - ArrayPool>.Shared.Return(syncNodesArray, true); + ArrayPool>.Shared.Return(syncNodesArray); } _context.CriticalSection.Leave(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 835bf5d40..12383fb8a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -143,7 +143,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading PreferredCore = cpuCore; AffinityMask |= 1UL << cpuCore; - SchedFlags = ThreadSchedState.None; + SchedFlags = type == ThreadType.Dummy + ? ThreadSchedState.Running + : ThreadSchedState.None; ActiveCore = cpuCore; ObjSyncResult = KernelResult.ThreadNotStarted; @@ -1053,7 +1055,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // If the thread is not schedulable, we want to just run or pause // it directly as we don't care about priority or the core it is // running on in this case. - if (SchedFlags == ThreadSchedState.Running) { _schedulerWaitEvent.Set(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs index e2dfd2ffb..83093570b 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs @@ -2,6 +2,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { enum ThreadType { + Dummy, Kernel, Kernel2, User, diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index bf0c7e9dc..91a8958e6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs @@ -1,10 +1,10 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Caps.Types; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System; using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Security.Cryptography; namespace Ryujinx.HLE.HOS.Services.Caps @@ -118,11 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps } // NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data. - 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); + Image.LoadPixelData(screenshotData, 1280, 720).SaveAsJpegAsync(filePath); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs index d7e276ea0..52412489a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs @@ -40,12 +40,5 @@ namespace Ryujinx.HLE.HOS.Services.Nim return ResultCode.Success; } - - [CommandCmif(5)] // 17.0.0+ - // CreateServerInterface2(pid, handle, u64) -> object - public ResultCode CreateServerInterface2(ServiceCtx context) - { - return CreateServerInterface(context); - } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/VsmsMappingArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/VsmsMappingArguments.cs index baada9197..1b56fcc44 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/VsmsMappingArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/VsmsMappingArguments.cs @@ -9,6 +9,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types public byte Sm0TpcIndex; public byte Sm1GpcIndex; public byte Sm1TpcIndex; - public uint Reserved; } } diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index f67699b90..5e18d7981 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -474,9 +474,9 @@ namespace Ryujinx.HLE.HOS.Services { const int MessageSize = 0x100; - using SpanOwner reqDataOwner = SpanOwner.Rent(MessageSize); + using IMemoryOwner reqDataOwner = ByteMemoryPool.Rent(MessageSize); - Span reqDataSpan = reqDataOwner.Span; + Span reqDataSpan = reqDataOwner.Memory.Span; _selfProcess.CpuMemory.Read(_selfThread.TlsAddress, reqDataSpan); diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 7a90c664e..3dc82035f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -2,7 +2,6 @@ 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; @@ -13,7 +12,7 @@ using System.Text; namespace Ryujinx.HLE.HOS.Services.Sm { - partial class IUserInterface : IpcService + class IUserInterface : IpcService { private static readonly Dictionary _services; @@ -96,7 +95,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm { ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name); - IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter); + IpcService service = serviceAttribute.Parameter != null + ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter) + : (IpcService)Activator.CreateInstance(type, context); service.TrySetServer(_commonServer); service.Server.AddSessionObj(session.ServerSession, service); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 2ffa961cb..7cb6763b8 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -85,9 +85,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ReadOnlySpan inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); - using SpanOwner outputParcelOwner = SpanOwner.RentCleared(checked((int)replySize)); + using IMemoryOwner outputParcelOwner = ByteMemoryPool.RentCleared(replySize); - Span outputParcel = outputParcelOwner.Span; + Span outputParcel = outputParcelOwner.Memory.Span; ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs index 1df280dce..c6cd60d04 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using System; +using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,9 +13,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { sealed class Parcel : IDisposable { - private readonly MemoryOwner _rawDataOwner; + private readonly IMemoryOwner _rawDataOwner; - private Span Raw => _rawDataOwner.Span; + private Span Raw => _rawDataOwner.Memory.Span; private ref ParcelHeader Header => ref MemoryMarshal.Cast(Raw)[0]; @@ -29,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public Parcel(ReadOnlySpan data) { - _rawDataOwner = MemoryOwner.RentCopy(data); + _rawDataOwner = ByteMemoryPool.RentCopy(data); _payloadPosition = 0; _objectPosition = 0; @@ -39,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { uint headerSize = (uint)Unsafe.SizeOf(); - _rawDataOwner = MemoryOwner.RentCleared(checked((int)BitUtils.AlignUp(headerSize + payloadSize + objectsSize, 4))); + _rawDataOwner = ByteMemoryPool.RentCleared(BitUtils.AlignUp(headerSize + payloadSize + objectsSize, 4)); Header.PayloadSize = payloadSize; Header.ObjectsSize = objectsSize; diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 4c17e7aed..fd517b1ae 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -412,9 +412,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat); - byte bytesPerPixel = + int bytesPerPixel = format == Format.B5G6R5Unorm || - format == Format.R4G4B4A4Unorm ? (byte)2 : (byte)4; + format == Format.R4G4B4A4Unorm ? 2 : 4; int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2; diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs index 8d828e8ed..9a5b6b0aa 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs @@ -15,12 +15,6 @@ namespace Ryujinx.HLE.Loaders.Npdm public ServiceAccessControl ServiceAccessControl { get; private set; } public KernelAccessControl KernelAccessControl { get; private set; } - /// The stream doesn't contain valid ACI0 data. - /// The stream does not support reading, is , or is already closed. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. - /// The FsAccessHeader.ContentOwnerId section is not implemented. public Aci0(Stream stream, int offset) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs index 57d0ee274..ab30b40ca 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs @@ -19,11 +19,6 @@ namespace Ryujinx.HLE.Loaders.Npdm public ServiceAccessControl ServiceAccessControl { get; private set; } public KernelAccessControl KernelAccessControl { get; private set; } - /// The stream doesn't contain valid ACID data. - /// The stream does not support reading, is , or is already closed. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public Acid(Stream stream, int offset) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs index a369f9f2d..f17ca348b 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs @@ -11,10 +11,6 @@ namespace Ryujinx.HLE.Loaders.Npdm public int Unknown3 { get; private set; } public int Unknown4 { get; private set; } - /// The stream does not support reading, is , or is already closed. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public FsAccessControl(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs index 249f8dd9d..5987be0ef 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs @@ -9,12 +9,6 @@ namespace Ryujinx.HLE.Loaders.Npdm public int Version { get; private set; } public ulong PermissionsBitmask { get; private set; } - /// The stream contains invalid data. - /// The ContentOwnerId section is not implemented. - /// The stream does not support reading, is , or is already closed. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public FsAccessHeader(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs index 979c6f669..171243799 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs @@ -6,10 +6,6 @@ namespace Ryujinx.HLE.Loaders.Npdm { public int[] Capabilities { get; private set; } - /// The stream does not support reading, is , or is already closed. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public KernelAccessControl(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs index 4a99de98c..622d7ee03 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs @@ -24,13 +24,6 @@ namespace Ryujinx.HLE.Loaders.Npdm public Aci0 Aci0 { get; private set; } public Acid Acid { get; private set; } - /// The stream doesn't contain valid NPDM data. - /// The FsAccessHeader.ContentOwnerId section is not implemented. - /// The stream does not support reading, is , or is already closed. - /// An error occured while reading bytes from the stream. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public Npdm(Stream stream) { BinaryReader reader = new(stream); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs index b6bc6492d..bb6df27fa 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs @@ -9,11 +9,6 @@ namespace Ryujinx.HLE.Loaders.Npdm { public IReadOnlyDictionary Services { get; private set; } - /// The stream does not support reading, is , or is already closed. - /// An error occured while reading bytes from the stream. - /// The end of the stream is reached. - /// The stream is closed. - /// An I/O error occurred. public ServiceAccessControl(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index 6c2a19894..6c2415e46 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -3,6 +3,7 @@ using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ncm; using LibHac.Ns; +using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; namespace Ryujinx.HLE.Loaders.Processes diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 2928ac7fe..e70fcb6fc 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -7,25 +7,16 @@ using LibHac.Ncm; using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; -using LibHac.Tools.Ncm; -using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; -using Ryujinx.HLE.Utilities; using System.IO; using System.Linq; using ApplicationId = LibHac.Ncm.ApplicationId; -using ContentType = LibHac.Ncm.ContentType; -using Path = System.IO.Path; namespace Ryujinx.HLE.Loaders.Processes.Extensions { - public static class NcaExtensions + static class NcaExtensions { - private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca) { // Extract RomFs and ExeFs from NCA. @@ -56,7 +47,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions nacpData = controlNca.GetNacp(device); } - /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update. + /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update. // Load program 0 control NCA as we are going to need it for display version. (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); @@ -95,11 +86,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return processResult; } - public static ulong GetProgramIdBase(this Nca nca) - { - return nca.Header.TitleId & ~0x1FFFUL; - } - public static int GetProgramIndex(this Nca nca) { return (int)(nca.Header.TitleId & 0xF); @@ -110,11 +96,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return nca.Header.ContentType == NcaContentType.Program; } - public static bool IsMain(this Nca nca) - { - return nca.IsProgram() && !nca.IsPatch(); - } - public static bool IsPatch(this Nca nca) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); @@ -127,43 +108,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return nca.Header.ContentType == NcaContentType.Control; } - public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath) - { - updatePath = null; - - // Load Update NCAs. - Nca updatePatchNca = null; - Nca updateControlNca = null; - - // Clear the program index part. - ulong titleIdBase = mainNca.GetProgramIdBase(); - - // Load update information if exists. - string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); - if (File.Exists(titleUpdateMetadataPath)) - { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected; - if (File.Exists(updatePath)) - { - IFileSystem updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem); - - foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel)) - { - if ((applicationTitleId & ~0x1FFFUL) != titleIdBase) - { - continue; - } - - updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex); - updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex); - break; - } - } - } - - return (updatePatchNca, updateControlNca); - } - public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null) { IFileSystem exeFs = null; @@ -228,31 +172,5 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return nacpData; } - - public static Cnmt GetCnmt(this Nca cnmtNca, IntegrityCheckLevel checkLevel, ContentMetaType metaType) - { - string path = $"/{metaType}_{cnmtNca.Header.TitleId:x16}.cnmt"; - using var cnmtFile = new UniqueRef(); - - try - { - Result result = cnmtNca.OpenFileSystem(0, checkLevel) - .OpenFile(ref cnmtFile.Ref, path.ToU8Span(), OpenMode.Read); - - if (result.IsSuccess()) - { - return new Cnmt(cnmtFile.Release().AsStream()); - } - } - catch (HorizonResultException ex) - { - if (!ResultFs.PathNotFound.Includes(ex.ResultValue)) - { - Logger.Warning?.Print(LogClass.Application, $"Failed get CNMT for '{cnmtNca.Header.TitleId:x16}' from NCA: {ex.Message}"); - } - } - - return null; - } } } diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs index b3590d9bd..e95b1b059 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -1,58 +1,26 @@ using LibHac.Common; -using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; -using LibHac.Ncm; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; -using LibHac.Tools.Ncm; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; -using ContentType = LibHac.Ncm.ContentType; namespace Ryujinx.HLE.Loaders.Processes.Extensions { public static class PartitionFileSystemExtensions { private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static Dictionary GetContentData(this IFileSystem partitionFileSystem, - ContentMetaType contentType, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel) - { - fileSystem.ImportTickets(partitionFileSystem); - - var programs = new Dictionary(); - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca")) - { - Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, contentType); - - if (cnmt == null) - { - continue; - } - - ContentMetaData content = new(partitionFileSystem, cnmt); - - if (content.Type != contentType) - { - continue; - } - - programs.TryAdd(content.ApplicationId, content); - } - - return programs; - } - - internal static (bool, ProcessResult) TryLoad(this PartitionFileSystemCore partitionFileSystem, Switch device, string path, ulong applicationId, out string errorMessage) + internal static (bool, ProcessResult) TryLoad(this PartitionFileSystemCore partitionFileSystem, Switch device, string path, out string errorMessage) where TMetaData : PartitionFileSystemMetaCore, new() where TFormat : IPartitionFileSystemFormat where THeader : unmanaged, IPartitionFileSystemHeader @@ -67,21 +35,30 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions try { - Dictionary applications = partitionFileSystem.GetContentData(ContentMetaType.Application, device.FileSystem, device.System.FsIntegrityCheckLevel); + device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem); - if (applicationId == 0) + // TODO: To support multi-games container, this should use CNMT NCA instead. + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) { - foreach ((ulong _, ContentMetaData content) in applications) + Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath); + + if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index) { - mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index); - controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index); - break; + continue; + } + + if (nca.IsPatch()) + { + patchNca = nca; + } + else if (nca.IsProgram()) + { + mainNca = nca; + } + else if (nca.IsControl()) + { + controlNca = nca; } - } - else if (applications.TryGetValue(applicationId, out ContentMetaData content)) - { - mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index); - controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index); } ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure(); @@ -102,7 +79,54 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return (false, ProcessResult.Failed); } - (Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _); + // Load Update NCAs. + Nca updatePatchNca = null; + Nca updateControlNca = null; + + if (ulong.TryParse(mainNca.Header.TitleId.ToString("x16"), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) + { + // Clear the program index part. + titleIdBase &= ~0xFUL; + + // Load update information if exists. + string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); + if (File.Exists(titleUpdateMetadataPath)) + { + string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected; + if (File.Exists(updatePath)) + { + PartitionFileSystem updatePartitionFileSystem = new(); + updatePartitionFileSystem.Initialize(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()).ThrowIfFailure(); + + device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem); + + // TODO: This should use CNMT NCA instead. + foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca")) + { + Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath); + + if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index) + { + continue; + } + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16")) + { + break; + } + + if (nca.IsProgram()) + { + updatePatchNca = nca; + } + else if (nca.IsControl()) + { + updateControlNca = nca; + } + } + } + } + } if (updatePatchNca != null) { @@ -114,11 +138,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions controlNca = updateControlNca; } + // Load contained DownloadableContents. // TODO: If we want to support multi-processes in future, we shouldn't clear AddOnContent data here. device.Configuration.ContentManager.ClearAocData(); + device.Configuration.ContentManager.AddAocData(partitionFileSystem, path, mainNca.Header.TitleId, device.Configuration.FsIntegrityCheckLevel); // Load DownloadableContents. - string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json"); + string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); if (File.Exists(addOnContentMetadataPath)) { List dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer); @@ -127,12 +153,9 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { - if (File.Exists(downloadableContentContainer.ContainerPath)) + if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled) { - if (downloadableContentNca.Enabled) - { - device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath); - } + device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath); } else { @@ -145,18 +168,18 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return (true, mainNca.Load(device, patchNca, controlNca)); } - errorMessage = $"Unable to load: Could not find Main NCA for title \"{applicationId:X16}\""; + errorMessage = "Unable to load: Could not find Main NCA"; return (false, ProcessResult.Failed); } - public static Nca GetNca(this IFileSystem fileSystem, KeySet keySet, string path) + public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path) { using var ncaFile = new UniqueRef(); fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - return new Nca(keySet, ncaFile.Release().AsStorage()); + return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage()); } } } diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index 12d9c8bd9..e5056c89a 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.Loaders.Processes _processesByPid = new ConcurrentDictionary(); } - public bool LoadXci(string path, ulong applicationId) + public bool LoadXci(string path) { FileStream stream = new(path, FileMode.Open, FileAccess.Read); Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage()); @@ -44,7 +44,7 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage); + (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage); if (!success) { @@ -66,13 +66,13 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - public bool LoadNsp(string path, ulong applicationId) + public bool LoadNsp(string path) { FileStream file = new(path, FileMode.Open, FileAccess.Read); PartitionFileSystem partitionFileSystem = new(); partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure(); - (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage); + (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage); if (processResult.ProcessId == 0) { diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index cf4eb416e..fe2aaac6d 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -43,14 +43,15 @@ namespace Ryujinx.HLE.Loaders.Processes foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) { - Nca nca = partitionFileSystem.GetNca(device.FileSystem.KeySet, fileEntry.FullPath); + Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath); - if (!nca.IsProgram()) + if (!nca.IsProgram() && nca.IsPatch()) { continue; } - ulong currentMainProgramId = nca.GetProgramIdBase(); + ulong currentProgramId = nca.Header.TitleId; + ulong currentMainProgramId = currentProgramId & ~0xFFFul; if (applicationId == 0 && currentMainProgramId != 0) { @@ -67,7 +68,7 @@ namespace Ryujinx.HLE.Loaders.Processes break; } - hasIndex[nca.GetProgramIndex()] = true; + hasIndex[(int)(currentProgramId & 0xF)] = true; } if (programCount == 0) diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index a7bb3cd7f..0fcf9e4b5 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -2,7 +2,6 @@ net8.0 - true @@ -12,7 +11,6 @@ - @@ -26,8 +24,8 @@ - - + + diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 9dfc69892..81c3ab473 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -73,9 +73,9 @@ namespace Ryujinx.HLE return Processes.LoadUnpackedNca(exeFsDir, romFsFile); } - public bool LoadXci(string xciFile, ulong applicationId = 0) + public bool LoadXci(string xciFile) { - return Processes.LoadXci(xciFile, applicationId); + return Processes.LoadXci(xciFile); } public bool LoadNca(string ncaFile) @@ -83,9 +83,9 @@ namespace Ryujinx.HLE return Processes.LoadNca(ncaFile); } - public bool LoadNsp(string nspFile, ulong applicationId = 0) + public bool LoadNsp(string nspFile) { - return Processes.LoadNsp(nspFile, applicationId); + return Processes.LoadNsp(nspFile); } public bool LoadProgram(string fileName) diff --git a/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs b/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs deleted file mode 100644 index 3c4ce0850..000000000 --- a/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs +++ /dev/null @@ -1,45 +0,0 @@ -using LibHac; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using Ryujinx.HLE.FileSystem; -using System.IO; - -namespace Ryujinx.HLE.Utilities -{ - public static class PartitionFileSystemUtils - { - public static IFileSystem OpenApplicationFileSystem(string path, VirtualFileSystem fileSystem, bool throwOnFailure = true) - { - FileStream file = File.OpenRead(path); - - IFileSystem partitionFileSystem; - - if (Path.GetExtension(path).ToLower() == ".xci") - { - partitionFileSystem = new Xci(fileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); - } - else - { - var pfsTemp = new PartitionFileSystem(); - Result initResult = pfsTemp.Initialize(file.AsStorage()); - - if (throwOnFailure) - { - initResult.ThrowIfFailure(); - } - else if (initResult.IsFailure()) - { - return null; - } - - partitionFileSystem = pfsTemp; - } - - fileSystem.ImportTickets(partitionFileSystem); - - return partitionFileSystem; - } - } -} diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index ef8849eea..ea2063758 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -219,7 +219,7 @@ namespace Ryujinx.Headless.SDL2 // Hacks - [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 8GiB.")] + [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")] public bool ExpandRAM { get; set; } [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 4ee271203..85aff6712 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.SystemInterop; @@ -19,7 +18,6 @@ using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless.SDL2.OpenGL; using Ryujinx.Headless.SDL2.Vulkan; using Ryujinx.HLE; @@ -90,11 +88,6 @@ namespace Ryujinx.Headless.SDL2 }; } - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - Parser.Default.ParseArguments(args) .WithParsed(Load) .WithNotParsed(errors => errors.Output()); @@ -464,8 +457,6 @@ namespace Ryujinx.Headless.SDL2 GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; - DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); - while (true) { LoadApplication(option); @@ -562,7 +553,7 @@ namespace Ryujinx.Headless.SDL2 _userChannelPersistence, renderer, new SDL2HardwareDeviceDriver(), - options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB, + options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, window, options.SystemLanguage, options.SystemRegion, diff --git a/src/Ryujinx.Horizon/Ngc/Ipc/Service.cs b/src/Ryujinx.Horizon/Ngc/Ipc/Service.cs index 740f893f8..828c09199 100644 --- a/src/Ryujinx.Horizon/Ngc/Ipc/Service.cs +++ b/src/Ryujinx.Horizon/Ngc/Ipc/Service.cs @@ -26,11 +26,7 @@ namespace Ryujinx.Horizon.Ngc.Ipc } [CmifCommand(1)] - public Result Check( - out uint checkMask, - [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan text, - uint regionMask, - ProfanityFilterOption option) + public Result Check(out uint checkMask, ReadOnlySpan text, uint regionMask, ProfanityFilterOption option) { lock (_profanityFilter) { diff --git a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj index bf34ddd17..d1f572d5c 100644 --- a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -16,4 +16,10 @@ + + + + NU1605 + + diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs index 2146362df..5d2798582 100644 --- a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs @@ -14,11 +14,6 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail { partial class HardwareOpusDecoder : IHardwareOpusDecoder, IDisposable { - static HardwareOpusDecoder() - { - OpusCodecFactory.AttemptToUseNativeLibrary = false; - } - [StructLayout(LayoutKind.Sequential)] private struct OpusPacketHeader { @@ -35,87 +30,60 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail } } - private interface IDecoder : IDisposable + private interface IDecoder { int SampleRate { get; } int ChannelsCount { get; } - int Decode(ReadOnlySpan inData, Span outPcm, int frameSize); + int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize); void ResetState(); } private class Decoder : IDecoder { - private readonly IOpusDecoder _decoder; + private readonly OpusDecoder _decoder; public int SampleRate => _decoder.SampleRate; public int ChannelsCount => _decoder.NumChannels; public Decoder(int sampleRate, int channelsCount) { - _decoder = OpusCodecFactory.CreateDecoder(sampleRate, channelsCount); + _decoder = new OpusDecoder(sampleRate, channelsCount); } - public int Decode(ReadOnlySpan inData, Span outPcm, int frameSize) + public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) { - return _decoder.Decode(inData, outPcm, frameSize); + return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize); } public void ResetState() { _decoder.ResetState(); } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _decoder?.Dispose(); - } - } } private class MultiSampleDecoder : IDecoder { - private readonly IOpusMultiStreamDecoder _decoder; + private readonly OpusMSDecoder _decoder; public int SampleRate => _decoder.SampleRate; - public int ChannelsCount => _decoder.NumChannels; + public int ChannelsCount { get; } public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping) { - _decoder = OpusCodecFactory.CreateMultiStreamDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); + ChannelsCount = channelsCount; + _decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); } - public int Decode(ReadOnlySpan inData, Span outPcm, int frameSize) + public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) { - return _decoder.DecodeMultistream(inData, outPcm, frameSize, false); + return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0); } public void ResetState() { _decoder.ResetState(); } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _decoder?.Dispose(); - } - } } private readonly IDecoder _decoder; @@ -253,8 +221,7 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail { timeTaken = 0; - Span outPcmSpace = MemoryMarshal.Cast(output); - Result result = DecodeInterleaved(_decoder, reset, input, outPcmSpace, output.Length, out outConsumed, out outSamples); + Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples); if (withPerf) { @@ -262,12 +229,14 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail timeTaken = 0; } + MemoryMarshal.Cast(outPcmData).CopyTo(output[..(outPcmData.Length * sizeof(short))]); + return result; } - private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, ReadOnlySpan packet) + private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, byte[] packet) { - int result = OpusPacketInfo.GetNumSamples(packet, decoder.SampleRate); + int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate); numSamples = result; @@ -287,11 +256,12 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail IDecoder decoder, bool reset, ReadOnlySpan input, - Span outPcmData, + out short[] outPcmData, int outputSize, out int outConsumed, out int outSamples) { + outPcmData = null; outConsumed = 0; outSamples = 0; @@ -311,7 +281,7 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail return CodecResult.InvalidLength; } - ReadOnlySpan opusData = input.Slice(headerSize, (int)header.Length); + byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray(); Result result = GetPacketNumSamples(decoder, out int numSamples, opusData); @@ -322,6 +292,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail return CodecResult.InvalidLength; } + outPcmData = new short[numSamples * decoder.ChannelsCount]; + if (reset) { decoder.ResetState(); @@ -329,22 +301,13 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail try { - outSamples = decoder.Decode(opusData, outPcmData, numSamples); + outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount); outConsumed = (int)totalSize; } - catch (OpusException e) + catch (OpusException) { - switch (e.OpusErrorCode) - { - case OpusError.OPUS_BUFFER_TOO_SMALL: - return CodecResult.InvalidLength; - case OpusError.OPUS_BAD_ARG: - return CodecResult.OpusBadArg; - case OpusError.OPUS_INVALID_PACKET: - return CodecResult.OpusInvalidPacket; - default: - return CodecResult.InvalidLength; - } + // TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases... + return CodecResult.InvalidLength; } } @@ -361,8 +324,6 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail _workBufferHandle = 0; } - - _decoder?.Dispose(); } } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index 406352003..2aefb0db5 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -21,8 +21,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl public long CurrentTime { get; private set; } - public IEnumerable MultiWaits => _multiWaits; - public MultiWaitImpl() { _multiWaits = new List(); diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs index 41d17802a..0e73e3f88 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs @@ -1,5 +1,4 @@ using Ryujinx.Horizon.Sdk.OsTypes.Impl; -using System.Collections.Generic; namespace Ryujinx.Horizon.Sdk.OsTypes { @@ -7,8 +6,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes { private readonly MultiWaitImpl _impl; - public IEnumerable MultiWaits => _impl.MultiWaits; - public MultiWait() { _impl = new MultiWaitImpl(); diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 570e3c802..9886e1cbf 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -3,7 +3,6 @@ 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 { @@ -117,18 +116,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc while (WaitAndProcessRequestsImpl()) { } - - // Unlink pending sessions, dispose expects them to be already unlinked. - - ServerSession[] serverSessions = Enumerable.OfType(_multiWait.MultiWaits).ToArray(); - - foreach (ServerSession serverSession in serverSessions) - { - if (serverSession.IsLinked) - { - serverSession.UnlinkFromMultiWaitHolder(); - } - } } public void WaitAndProcessRequests() diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 1dc87358d..2409ecf22 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -176,7 +176,7 @@ namespace Ryujinx.Input.HLE { foreach (InputConfig inputConfig in _inputConfig) { - _controllers[(int)inputConfig.PlayerIndex]?.GamepadDriver?.Clear(); + _controllers[(int)inputConfig.PlayerIndex].GamepadDriver.Clear(); } _blockInputUpdates = false; diff --git a/src/Ryujinx.Input/IGamepadDriver.cs b/src/Ryujinx.Input/IGamepadDriver.cs index 625c3e694..ff4d36993 100644 --- a/src/Ryujinx.Input/IGamepadDriver.cs +++ b/src/Ryujinx.Input/IGamepadDriver.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Input IGamepad GetGamepad(string id); /// - /// Clear the internal state of the driver. + /// Flush the internal state of the driver. /// /// Does nothing by default. void Clear() { } diff --git a/src/Ryujinx.Memory/Range/IMultiRangeItem.cs b/src/Ryujinx.Memory/Range/IMultiRangeItem.cs index 5f9611c75..87fde2465 100644 --- a/src/Ryujinx.Memory/Range/IMultiRangeItem.cs +++ b/src/Ryujinx.Memory/Range/IMultiRangeItem.cs @@ -4,22 +4,6 @@ namespace Ryujinx.Memory.Range { MultiRange Range { get; } - 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; - } - } + ulong BaseAddress => Range.GetSubRange(0).Address; } } diff --git a/src/Ryujinx.Memory/Range/MemoryRange.cs b/src/Ryujinx.Memory/Range/MemoryRange.cs index 20e9d00bb..46aca9ba0 100644 --- a/src/Ryujinx.Memory/Range/MemoryRange.cs +++ b/src/Ryujinx.Memory/Range/MemoryRange.cs @@ -5,11 +5,6 @@ namespace Ryujinx.Memory.Range /// public readonly record struct MemoryRange { - /// - /// Special address value used to indicate than an address is invalid. - /// - internal const ulong InvalidAddress = ulong.MaxValue; - /// /// An empty memory range, with a null address and zero size. /// @@ -63,24 +58,13 @@ namespace Ryujinx.Memory.Range return thisAddress < otherEndAddress && otherAddress < thisEndAddress; } - /// - /// Checks if a given sub-range of memory is invalid. - /// Those are used to represent unmapped memory regions (holes in the region mapping). - /// - /// Memory range to check - /// True if the memory range is considered invalid, false otherwise - internal static bool IsInvalid(ref MemoryRange subRange) - { - return subRange.Address == InvalidAddress; - } - /// /// Returns a string summary of the memory range. /// /// A string summary of the memory range public override string ToString() { - if (Address == InvalidAddress) + if (Address == ulong.MaxValue) { return $"[Unmapped 0x{Size:X}]"; } diff --git a/src/Ryujinx.Memory/Range/MultiRangeList.cs b/src/Ryujinx.Memory/Range/MultiRangeList.cs index c3c6ae797..1804ff5c8 100644 --- a/src/Ryujinx.Memory/Range/MultiRangeList.cs +++ b/src/Ryujinx.Memory/Range/MultiRangeList.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Memory.Range { var subrange = range.GetSubRange(i); - if (MemoryRange.IsInvalid(ref subrange)) + if (IsInvalid(ref subrange)) { continue; } @@ -56,7 +56,7 @@ namespace Ryujinx.Memory.Range { var subrange = range.GetSubRange(i); - if (MemoryRange.IsInvalid(ref subrange)) + if (IsInvalid(ref subrange)) { continue; } @@ -99,7 +99,7 @@ namespace Ryujinx.Memory.Range { var subrange = range.GetSubRange(i); - if (MemoryRange.IsInvalid(ref subrange)) + if (IsInvalid(ref subrange)) { continue; } @@ -142,6 +142,17 @@ namespace Ryujinx.Memory.Range return overlapCount; } + /// + /// Checks if a given sub-range of memory is invalid. + /// Those are used to represent unmapped memory regions (holes in the region mapping). + /// + /// Memory range to checl + /// True if the memory range is considered invalid, false otherwise + private static bool IsInvalid(ref MemoryRange subRange) + { + return subRange.Address == ulong.MaxValue; + } + /// /// Gets all items on the list starting at the specified memory address. /// diff --git a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs index f41072244..506e25f66 100644 --- a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs +++ b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs @@ -130,9 +130,9 @@ namespace Ryujinx.Memory } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(size); + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size); - Read(va, memoryOwner.Span); + Read(va, memoryOwner.Memory.Span); return new WritableRegion(this, va, memoryOwner); } diff --git a/src/Ryujinx.Memory/WritableRegion.cs b/src/Ryujinx.Memory/WritableRegion.cs index 54facb508..2c21ef4e8 100644 --- a/src/Ryujinx.Memory/WritableRegion.cs +++ b/src/Ryujinx.Memory/WritableRegion.cs @@ -1,5 +1,5 @@ -using Ryujinx.Common.Memory; using System; +using System.Buffers; namespace Ryujinx.Memory { @@ -7,7 +7,7 @@ namespace Ryujinx.Memory { private readonly IWritableBlock _block; private readonly ulong _va; - private readonly MemoryOwner _memoryOwner; + private readonly IMemoryOwner _memoryOwner; private readonly bool _tracked; private bool NeedsWriteback => _block != null; @@ -22,7 +22,7 @@ namespace Ryujinx.Memory Memory = memory; } - public WritableRegion(IWritableBlock block, ulong va, MemoryOwner memoryOwner, bool tracked = false) + public WritableRegion(IWritableBlock block, ulong va, IMemoryOwner memoryOwner, bool tracked = false) : this(block, va, memoryOwner.Memory, tracked) { _memoryOwner = memoryOwner; diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 9827156d0..ed6d94190 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -53,7 +53,6 @@ namespace Ryujinx.SDL2.Common return; } - SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs index a84d7b466..d2c6bd59e 100644 --- a/src/Ryujinx.ShaderTools/Program.cs +++ b/src/Ryujinx.ShaderTools/Program.cs @@ -25,32 +25,32 @@ namespace Ryujinx.ShaderTools _imagesCount = 0; } - public SetBindingPair CreateConstantBufferBinding(int index) + public int CreateConstantBufferBinding(int index) { - return new SetBindingPair(0, index + 1); + return index + 1; } - public SetBindingPair CreateImageBinding(int count, bool isBuffer) + public int CreateImageBinding(int count, bool isBuffer) { int binding = _imagesCount; _imagesCount += count; - return new SetBindingPair(3, binding); + return binding; } - public SetBindingPair CreateStorageBufferBinding(int index) + public int CreateStorageBufferBinding(int index) { - return new SetBindingPair(1, index); + return index; } - public SetBindingPair CreateTextureBinding(int count, bool isBuffer) + public int CreateTextureBinding(int count, bool isBuffer) { int binding = _texturesCount; _texturesCount += count; - return new SetBindingPair(2, binding); + return binding; } public ReadOnlySpan GetCode(ulong address, int minimumSize) diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs index 3e48a5b4e..557581881 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs @@ -52,9 +52,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -80,9 +78,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -108,9 +104,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -136,9 +130,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -164,9 +156,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -192,9 +182,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -220,9 +208,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -248,9 +234,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -276,9 +260,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -304,69 +286,11 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsTrue(behaviourContext.IsBiquadFilterGroupedOptimizationSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); } - - [Test] - public void TestRevision11() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision11); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision12() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision12); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs index 80b801336..ad974aab1 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs @@ -9,8 +9,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void EnsureTypeSize() { - Assert.AreEqual(0xE0, Unsafe.SizeOf()); - Assert.AreEqual(0x110, Unsafe.SizeOf()); + Assert.AreEqual(0xE0, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs index 1e66d8112..41365c624 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs @@ -25,25 +25,6 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] UQAddSub16() - { - return new[] - { - 0xe6200f10u, // QADD16 R0, R0, R0 - 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[] @@ -201,42 +182,6 @@ 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, diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs index 08202c9e1..6087a6834 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs @@ -327,55 +327,6 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - - [Test, Pairwise, Description("VSHLL. {}, , #")] - public void Vshll([Values(0u, 2u)] uint rd, - [Values(1u, 0u)] uint rm, - [Values(0u, 1u, 2u)] uint size, - [Random(RndCnt)] ulong z, - [Random(RndCnt)] ulong a, - [Random(RndCnt)] ulong b) - { - uint opcode = 0xf3b20300u; // VSHLL.I8 Q0, D0, #8 - - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); - opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= size << 18; - - 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, 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 } } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index 843273dc2..38e08bf89 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -909,39 +909,6 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise, Description("VQRDMULH. , , ")] - 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) // - { - 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, diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs index 7375f4d55..39b50867f 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs @@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("VSHL. {}, , #")] - public void Vshl_Imm([Values(0u, 1u)] uint rd, + public void Vshl_Imm([Values(0u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0u, 1u, 2u, 3u)] uint size, [Random(RndCntShiftImm)] uint shiftImm, @@ -262,40 +262,6 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise, Description("VSLI. {}, , #")] - 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, diff --git a/src/Ryujinx.UI.Common/App/ApplicationData.cs b/src/Ryujinx.UI.Common/App/ApplicationData.cs index 08bd2677d..8cc7238e9 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationData.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationData.cs @@ -9,11 +9,9 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.UI.Common.Helper; using System; using System.IO; -using System.Text.Json.Serialization; namespace Ryujinx.UI.App.Common { @@ -21,10 +19,10 @@ namespace Ryujinx.UI.App.Common { public bool Favorite { get; set; } public byte[] Icon { get; set; } - public string Name { get; set; } = "Unknown"; - public ulong Id { get; set; } - public string Developer { get; set; } = "Unknown"; - public string Version { get; set; } = "0"; + public string TitleName { get; set; } + public string TitleId { get; set; } + public string Developer { get; set; } + public string Version { get; set; } public TimeSpan TimePlayed { get; set; } public DateTime? LastPlayed { get; set; } public string FileExtension { get; set; } @@ -38,13 +36,7 @@ namespace Ryujinx.UI.App.Common public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); - [JsonIgnore] public string IdString => Id.ToString("x16"); - - [JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL; - - [JsonIgnore] public string IdBaseString => IdBase.ToString("x16"); - - public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath) + public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath) { using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); @@ -53,7 +45,7 @@ namespace Ryujinx.UI.App.Common if (!System.IO.Path.Exists(titleFilePath)) { - Logger.Error?.Print(LogClass.Application, $"File \"{titleFilePath}\" does not exist."); + Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}"); return string.Empty; } @@ -113,7 +105,7 @@ namespace Ryujinx.UI.App.Common return string.Empty; } - (Nca updatePatchNca, _) = mainNca.GetUpdateData(virtualFileSystem, checkLevel, 0, out string _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); if (updatePatchNca != null) { diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs index 2defc1f6c..65cf7a9e6 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs @@ -4,29 +4,28 @@ using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; -using LibHac.Ncm; using LibHac.Ns; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration.System; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; -using ContentType = LibHac.Ncm.ContentType; using Path = System.IO.Path; using TimeSpan = System.TimeSpan; @@ -34,7 +33,6 @@ namespace Ryujinx.UI.App.Common { public class ApplicationLibrary { - public Language DesiredLanguage { get; set; } public event EventHandler ApplicationAdded; public event EventHandler ApplicationCountUpdated; @@ -45,15 +43,15 @@ namespace Ryujinx.UI.App.Common private readonly byte[] _nsoIcon; private readonly VirtualFileSystem _virtualFileSystem; - private readonly IntegrityCheckLevel _checkLevel; + private Language _desiredTitleLanguage; private CancellationTokenSource _cancellationToken; private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public ApplicationLibrary(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel) + public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { _virtualFileSystem = virtualFileSystem; - _checkLevel = checkLevel; _nspIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSP.png"); _xciIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_XCI.png"); @@ -67,302 +65,270 @@ namespace Ryujinx.UI.App.Common Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); byte[] resourceByteArray = new byte[resourceStream.Length]; - resourceStream.ReadExactly(resourceByteArray); + resourceStream.Read(resourceByteArray); return resourceByteArray; } - /// The npdm file doesn't contain valid data. - /// The FsAccessHeader.ContentOwnerId section is not implemented. - /// An error occured while reading bytes from the stream. - /// The end of the stream is reached. - /// An I/O error occurred. - private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath) + public void CancelLoading() { - ApplicationData data = new() - { - Icon = _nspIcon, - Path = filePath, - }; - - using UniqueRef npdmFile = new(); - - Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - if (ResultFs.PathNotFound.Includes(result)) - { - Npdm npdm = new(npdmFile.Get.AsStream()); - - data.Name = npdm.TitleName; - data.Id = npdm.Aci0.TitleId; - } - - return data; + _cancellationToken?.Cancel(); } - /// The configured key set is missing a key. - /// The NCA header could not be decrypted. - /// The NCA version is not supported. - /// An error occured while reading PFS data. - /// The npdm file doesn't contain valid data. - /// The FsAccessHeader.ContentOwnerId section is not implemented. - /// An error occured while reading bytes from the stream. - /// The end of the stream is reached. - /// An I/O error occurred. - private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath) + public static void ReadControlData(IFileSystem controlFs, Span outProperty) { - bool isExeFs = false; + using UniqueRef controlFile = new(); - // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. - bool hasMainNca = false; + controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); + } - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) + public void LoadApplications(List appDirs, Language desiredTitleLanguage) + { + int numApplicationsFound = 0; + int numApplicationsLoaded = 0; + + _desiredTitleLanguage = desiredTitleLanguage; + + _cancellationToken = new CancellationTokenSource(); + + // Builds the applications list with paths to found applications + List applications = new(); + + try { - if (Path.GetExtension(fileEntry.FullPath)?.ToLower() == ".nca") + foreach (string appDir in appDirs) { - using UniqueRef ncaFile = new(); + if (_cancellationToken.Token.IsCancellationRequested) + { + return; + } + + if (!Directory.Exists(appDir)) + { + Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"{ReleaseInformation.ConfigName}\" contains an invalid directory: \"{appDir}\""); + + continue; + } try { - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - // Some main NCAs don't have a data partition, so check if the partition exists before opening it - if (nca.Header.ContentType == NcaContentType.Program && - !(nca.SectionExists(NcaSectionType.Data) && - nca.Header.GetFsHeader(dataIndex).IsPatchSection())) + IEnumerable files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => { - hasMainNca = true; + return + (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || + (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value) || + (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value) || + (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value) || + (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value) || + (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value); + }); - break; - } - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Encountered an error while trying to load applications from file '{filePath}': {exception}"); - - return null; - } - } - else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") - { - isExeFs = true; - } - } - - if (hasMainNca) - { - List applications = GetApplicationsFromPfs(pfs, filePath); - - switch (applications.Count) - { - case 1: - return applications[0]; - case >= 1: - Logger.Warning?.Print(LogClass.Application, $"File '{filePath}' contains more applications than expected: {applications.Count}"); - return applications[0]; - default: - return null; - } - } - - if (isExeFs) - { - return GetApplicationFromExeFs(pfs, filePath); - } - - return null; - } - - /// The configured key set is missing a key. - /// The NCA header could not be decrypted. - /// The NCA version is not supported. - /// An error occured while reading PFS data. - private List GetApplicationsFromPfs(IFileSystem pfs, string filePath) - { - var applications = new List(); - string extension = Path.GetExtension(filePath).ToLower(); - - foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel)) - { - ApplicationData applicationData = new() - { - Id = titleId, - Path = filePath, - }; - - Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program); - Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control); - - BlitStruct 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 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("/", "*")) - { - if (entry.Name == "control.nacp") + foreach (string app in files) { - continue; - } - - using var icon = new UniqueRef(); - - 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; - } - } - - applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon; - } - - applicationData.ControlHolder = controlHolder; - - applications.Add(applicationData); - } - - return applications; - } - - public bool TryGetApplicationsFromFile(string applicationPath, out List applications) - { - applications = []; - long fileSize; - - try - { - fileSize = new FileInfo(applicationPath).Length; - } - catch (FileNotFoundException) - { - Logger.Warning?.Print(LogClass.Application, $"The file was not found: '{applicationPath}'"); - - return false; - } - - BlitStruct controlHolder = new(1); - - try - { - string extension = Path.GetExtension(applicationPath).ToLower(); - - using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); - - switch (extension) - { - case ".xci": - { - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - - applications = GetApplicationsFromPfs(xci.OpenPartition(XciPartitionType.Secure), applicationPath); - - if (applications.Count == 0) + if (_cancellationToken.Token.IsCancellationRequested) { - return false; + return; } - break; - } - case ".nsp": - case ".pfs0": - { - var pfs = new PartitionFileSystem(); - pfs.Initialize(file.AsStorage()).ThrowIfFailure(); + var fileInfo = new FileInfo(app); + string extension = fileInfo.Extension.ToLower(); - ApplicationData result = GetApplicationFromNsp(pfs, applicationPath); - - if (result == null) + if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") { - return false; - } + var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; - applications.Add(result); - - break; - } - case ".nro": - { - BinaryReader reader = new(file); - ApplicationData application = new(); - - file.Seek(24, SeekOrigin.Begin); - - int assetOffset = reader.ReadInt32(); - - if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") - { - 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) + if (!File.Exists(fullPath)) { - application.Icon = Read(assetOffset + iconOffset, (int)iconSize); + Logger.Warning?.Print(LogClass.Application, $"Skipping invalid symlink: {fileInfo.FullName}"); + continue; + } + + applications.Add(fullPath); + numApplicationsFound++; + } + } + } + catch (UnauthorizedAccessException) + { + Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{appDir}\""); + } + } + + // Loops through applications list, creating a struct and then firing an event containing the struct for each application + foreach (string applicationPath in applications) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return; + } + + long fileSize = new FileInfo(applicationPath).Length; + string titleName = "Unknown"; + string titleId = "0000000000000000"; + string developer = "Unknown"; + string version = "0"; + byte[] applicationIcon = null; + + BlitStruct controlHolder = new(1); + + try + { + string extension = Path.GetExtension(applicationPath).ToLower(); + + using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); + + if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + { + try + { + IFileSystem pfs; + + bool isExeFs = false; + + if (extension == ".xci") + { + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); + + pfs = xci.OpenPartition(XciPartitionType.Secure); } else { - application.Icon = _nroIcon; + var pfsTemp = new PartitionFileSystem(); + pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure(); + pfs = pfsTemp; + + // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. + bool hasMainNca = false; + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) + { + if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") + { + using UniqueRef ncaFile = new(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + // Some main NCAs don't have a data partition, so check if the partition exists before opening it + if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) + { + hasMainNca = true; + + break; + } + } + else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") + { + isExeFs = true; + } + } + + if (!hasMainNca && !isExeFs) + { + numApplicationsFound--; + + continue; + } } - // Read the NACP data - Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); + if (isExeFs) + { + applicationIcon = _nspIcon; - GetApplicationInformation(ref controlHolder.Value, ref application); + using UniqueRef npdmFile = new(); + + Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); + + if (ResultFs.PathNotFound.Includes(result)) + { + Npdm npdm = new(npdmFile.Get.AsStream()); + + titleName = npdm.TitleName; + titleId = npdm.Aci0.TitleId.ToString("x16"); + } + } + else + { + GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); + + // Check if there is an update available. + if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs)) + { + // Replace the original ControlFs by the updated one. + controlFs = updatedControlFs; + } + + ReadControlData(controlFs, controlHolder.ByteSpan); + + GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version); + + // Read the icon from the ControlFS and store it as a byte array + try + { + using UniqueRef icon = new(); + + controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using MemoryStream stream = new(); + + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); + } + catch (HorizonResultException) + { + foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) + { + if (entry.Name == "control.nacp") + { + continue; + } + + using var icon = new UniqueRef(); + + controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using MemoryStream stream = new(); + + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); + + if (applicationIcon != null) + { + break; + } + } + + applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; + } + } } - else + catch (MissingKeyException exception) { - application.Icon = _nroIcon; - application.Name = Path.GetFileNameWithoutExtension(applicationPath); + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; + + Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } + catch (InvalidDataException) + { + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - application.ControlHolder = controlHolder; - applications.Add(application); + 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}"); + } + catch (Exception exception) + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); - break; + numApplicationsFound--; + + continue; + } + } + else if (extension == ".nro") + { + BinaryReader reader = new(file); byte[] Read(long position, int size) { @@ -370,74 +336,102 @@ namespace Ryujinx.UI.App.Common return reader.ReadBytes(size); } - } - case ".nca": - { - ApplicationData application = new(); - Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); - - if (!nca.IsProgram() || nca.IsPatch()) + try { - return false; + file.Seek(24, SeekOrigin.Begin); + + int assetOffset = reader.ReadInt32(); + + if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") + { + 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) + { + applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + } + else + { + applicationIcon = _nroIcon; + } + + // Read the NACP data + Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); + + GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version); + } + else + { + applicationIcon = _nroIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); + } + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); + + numApplicationsFound--; + + continue; + } + } + else if (extension == ".nca") + { + try + { + Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) + { + numApplicationsFound--; + + continue; + } + } + 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}"); + + numApplicationsFound--; + + continue; } - application.Icon = _ncaIcon; - application.Name = Path.GetFileNameWithoutExtension(applicationPath); - application.ControlHolder = controlHolder; - - applications.Add(application); - - break; + applicationIcon = _ncaIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); } - // If its an NSO we just set defaults - case ".nso": + // If its an NSO we just set defaults + else if (extension == ".nso") { - ApplicationData application = new() - { - Icon = _nsoIcon, - Name = Path.GetFileNameWithoutExtension(applicationPath), - }; - - applications.Add(application); - - break; + applicationIcon = _nsoIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); } - } - } - 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) - { - // Only load metadata for applications with an ID - if (data.Id != 0) - { - ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata => + } + catch (IOException exception) { - appMetadata.Title = data.Name; + Logger.Warning?.Print(LogClass.Application, exception.Message); + + numApplicationsFound--; + + continue; + } + + ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata => + { + appMetadata.Title = titleName; // 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) @@ -461,134 +455,28 @@ namespace Ryujinx.UI.App.Common } }); - 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; - } - - return true; - } - - public void CancelLoading() - { - _cancellationToken?.Cancel(); - } - - public static void ReadControlData(IFileSystem controlFs, Span outProperty) - { - using UniqueRef controlFile = new(); - - controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); - } - - public void LoadApplications(List appDirs) - { - int numApplicationsFound = 0; - int numApplicationsLoaded = 0; - - _cancellationToken = new CancellationTokenSource(); - - // Builds the applications list with paths to found applications - List applicationPaths = new(); - - try - { - foreach (string appDir in appDirs) - { - if (_cancellationToken.Token.IsCancellationRequested) + ApplicationData data = new() { - return; - } + Favorite = appMetadata.Favorite, + Icon = applicationIcon, + TitleName = titleName, + TitleId = titleId, + Developer = developer, + Version = version, + TimePlayed = appMetadata.TimePlayed, + LastPlayed = appMetadata.LastPlayed, + FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper(), + FileSize = fileSize, + Path = applicationPath, + ControlHolder = controlHolder, + }; - if (!Directory.Exists(appDir)) + numApplicationsLoaded++; + + OnApplicationAdded(new ApplicationAddedEventArgs { - Logger.Warning?.Print(LogClass.Application, $"The specified game directory \"{appDir}\" does not exist."); - - continue; - } - - try - { - EnumerationOptions options = new() - { - RecurseSubdirectories = true, - IgnoreInaccessible = false, - }; - - IEnumerable files = Directory.EnumerateFiles(appDir, "*", options).Where(file => - { - return - (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || - (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value) || - (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value) || - (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value) || - (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value) || - (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value); - }); - - foreach (string app in files) - { - if (_cancellationToken.Token.IsCancellationRequested) - { - return; - } - - var fileInfo = new FileInfo(app); - - 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) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{appDir}\""); - } - } - - // Loops through applications list, creating a struct and then firing an event containing the struct for each application - foreach (string applicationPath in applicationPaths) - { - if (_cancellationToken.Token.IsCancellationRequested) - { - return; - } - - if (TryGetApplicationsFromFile(applicationPath, out List applications)) - { - foreach (var application in applications) - { - OnApplicationAdded(new ApplicationAddedEventArgs - { - AppData = application, - }); - } - - if (applications.Count > 1) - { - numApplicationsFound += applications.Count - 1; - } - - numApplicationsLoaded += applications.Count; - } - else - { - numApplicationsFound--; - } + AppData = data, + }); OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs { @@ -620,6 +508,15 @@ namespace Ryujinx.UI.App.Common ApplicationCountUpdated?.Invoke(null, e); } + private void GetControlFsAndTitleId(IFileSystem pfs, out IFileSystem controlFs, out string titleId) + { + (_, _, Nca controlNca) = GetGameData(_virtualFileSystem, pfs, 0); + + // Return the ControlFS + controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); + titleId = controlNca?.Header.TitleId.ToString("x16"); + } + public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action modifyFunction = null) { string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui"); @@ -657,29 +554,10 @@ namespace Ryujinx.UI.App.Common return appMetadata; } - public byte[] GetApplicationIcon(string applicationPath, Language desiredTitleLanguage, ulong applicationId) + public byte[] GetApplicationIcon(string applicationPath, Language desiredTitleLanguage) { byte[] applicationIcon = null; - if (applicationId == 0) - { - if (Directory.Exists(applicationPath)) - { - return _ncaIcon; - } - - return Path.GetExtension(applicationPath).ToLower() switch - { - ".nsp" => _nspIcon, - ".pfs0" => _nspIcon, - ".xci" => _xciIcon, - ".nso" => _nsoIcon, - ".nro" => _nroIcon, - ".nca" => _ncaIcon, - _ => _ncaIcon, - }; - } - try { // Look for icon only if applicationPath is not a directory @@ -725,16 +603,7 @@ namespace Ryujinx.UI.App.Common else { // Store the ControlFS in variable called controlFs - Dictionary programs = pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel); - IFileSystem controlFs = null; - - if (programs.TryGetValue(applicationId, out ContentMetaData value)) - { - if (value.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control) is { } controlNca) - { - controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - } - } + GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); // Read the icon from the ControlFS and store it as a byte array try @@ -761,11 +630,16 @@ namespace Ryujinx.UI.App.Common controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - using MemoryStream stream = new(); - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); + using (MemoryStream stream = new()) + { + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); + } - break; + if (applicationIcon != null) + { + break; + } } applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; @@ -848,79 +722,80 @@ namespace Ryujinx.UI.App.Common return applicationIcon ?? _ncaIcon; } - private void GetApplicationInformation(ref ApplicationControlProperty controlData, ref ApplicationData data) + private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version) { - _ = Enum.TryParse(DesiredLanguage.ToString(), out TitleLanguage desiredTitleLanguage); + _ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage); if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage) { - data.Name = controlData.Title[(int)desiredTitleLanguage].NameString.ToString(); - data.Developer = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString(); + titleName = controlData.Title[(int)desiredTitleLanguage].NameString.ToString(); + publisher = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString(); } else { - data.Name = null; - data.Developer = null; + titleName = null; + publisher = null; } - if (string.IsNullOrWhiteSpace(data.Name)) + if (string.IsNullOrWhiteSpace(titleName)) { foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) { if (!controlTitle.NameString.IsEmpty()) { - data.Name = controlTitle.NameString.ToString(); + titleName = controlTitle.NameString.ToString(); break; } } } - if (string.IsNullOrWhiteSpace(data.Developer)) + if (string.IsNullOrWhiteSpace(publisher)) { foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) { if (!controlTitle.PublisherString.IsEmpty()) { - data.Developer = controlTitle.PublisherString.ToString(); + publisher = controlTitle.PublisherString.ToString(); break; } } } - if (data.Id == 0) + if (controlData.PresenceGroupId != 0) { - if (controlData.SaveDataOwnerId != 0) - { - data.Id = controlData.SaveDataOwnerId; - } - else if (controlData.PresenceGroupId != 0) - { - data.Id = controlData.PresenceGroupId; - } - else if (controlData.AddOnContentBaseId != 0) - { - data.Id = (controlData.AddOnContentBaseId - 0x1000); - } + titleId = controlData.PresenceGroupId.ToString("x16"); + } + else if (controlData.SaveDataOwnerId != 0) + { + titleId = controlData.SaveDataOwnerId.ToString(); + } + else if (controlData.AddOnContentBaseId != 0) + { + titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16"); + } + else + { + titleId = "0000000000000000"; } - data.Version = controlData.DisplayVersionString.ToString(); + version = controlData.DisplayVersionString.ToString(); } - private bool IsUpdateApplied(Nca mainNca, out IFileSystem updatedControlFs) + private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs) { updatedControlFs = null; - string updatePath = null; + string updatePath = "(unknown)"; try { - (Nca patchNca, Nca controlNca) = mainNca.GetUpdateData(_virtualFileSystem, _checkLevel, 0, out updatePath); + (Nca patchNca, Nca controlNca) = GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); if (patchNca != null && controlNca != null) { - updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); + updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); return true; } @@ -936,5 +811,120 @@ namespace Ryujinx.UI.App.Common return false; } + + public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, IFileSystem pfs, int programIndex) + { + Nca mainNca = null; + Nca patchNca = null; + Nca controlNca = null; + + fileSystem.ImportTickets(pfs); + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage()); + + int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); + + if (ncaProgramIndex != programIndex) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.Program) + { + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) + { + patchNca = nca; + } + else + { + mainNca = nca; + } + } + else if (nca.Header.ContentType == NcaContentType.Control) + { + controlNca = nca; + } + } + + return (mainNca, patchNca, controlNca); + } + + public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) + { + Nca patchNca = null; + Nca controlNca = null; + + fileSystem.ImportTickets(pfs); + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage()); + + int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); + + if (ncaProgramIndex != programIndex) + { + continue; + } + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) + { + break; + } + + if (nca.Header.ContentType == NcaContentType.Program) + { + patchNca = nca; + } + else if (nca.Header.ContentType == NcaContentType.Control) + { + controlNca = nca; + } + } + + return (patchNca, controlNca); + } + + public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath) + { + updatePath = null; + + if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) + { + // Clear the program index part. + titleIdBase &= ~0xFUL; + + // Load update information if exists. + string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); + + if (File.Exists(titleUpdateMetadataPath)) + { + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected; + + if (File.Exists(updatePath)) + { + FileStream file = new(updatePath, FileMode.Open, FileAccess.Read); + PartitionFileSystem nsp = new(); + nsp.Initialize(file.AsStorage()).ThrowIfFailure(); + + return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); + } + } + } + + return (null, null); + } } } diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index 8a0be4028..af3ad0a1d 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -238,7 +238,7 @@ namespace Ryujinx.UI.Common.Configuration public MemoryManagerMode MemoryManagerMode { get; set; } /// - /// Expands the RAM amount on the emulated system from 4GiB to 8GiB + /// Expands the RAM amount on the emulated system from 4GiB to 6GiB /// public bool ExpandRam { get; set; } diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index 8420dc5d9..01c60a5e2 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -11,7 +11,6 @@ using Ryujinx.UI.Common.Configuration.UI; using Ryujinx.UI.Common.Helper; using System; using System.Collections.Generic; -using System.Globalization; using System.Text.Json.Nodes; namespace Ryujinx.UI.Common.Configuration @@ -1595,9 +1594,7 @@ namespace Ryujinx.UI.Common.Configuration private static void LogValueChange(ReactiveEventArgs eventArgs, string valueName) { - string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}"); - - Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, message); + Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"{valueName} set to: {eventArgs.NewValue}"); } public static void Initialize() diff --git a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs index 6966038b6..fb07195d0 100644 --- a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs @@ -104,13 +104,8 @@ namespace Ryujinx.UI.Common // Find the length to trim the string to guarantee we have space for the trailing ellipsis. int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis); - // Make sure the string is long enough to perform the basic trim. - // Amount of bytes != Length of the string - if (input.Length > trimLimit) - { - // Basic trim to best case scenario of 1 byte characters. - input = input[..trimLimit]; - } + // Basic trim to best case scenario of 1 byte characters. + input = input[..trimLimit]; while (Encoding.UTF8.GetByteCount(input) > trimLimit) { diff --git a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs index ae0e4d904..bbacd5fec 100644 --- a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs @@ -14,7 +14,6 @@ namespace Ryujinx.UI.Common.Helper public static string BaseDirPathArg { get; private set; } public static string Profile { get; private set; } public static string LaunchPathArg { get; private set; } - public static string LaunchApplicationId { get; private set; } public static bool StartFullscreenArg { get; private set; } public static void ParseArguments(string[] args) @@ -73,10 +72,6 @@ namespace Ryujinx.UI.Common.Helper OverrideGraphicsBackend = args[++i]; break; - case "-i": - case "--application-id": - LaunchApplicationId = args[++i]; - break; case "--docked-mode": OverrideDockedMode = true; break; diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs index 1849f40cb..c2085b28c 100644 --- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs @@ -1,7 +1,10 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using ShellLink; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using System; using System.Collections.Generic; using System.IO; @@ -12,39 +15,37 @@ namespace Ryujinx.UI.Common.Helper public static class ShortcutHelper { [SupportedOSPlatform("windows")] - private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath) + private static void CreateShortcutWindows(string applicationFilePath, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath) { string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe"); iconPath += ".ico"; MemoryStream iconDataStream = new(iconData); - using var image = SKBitmap.Decode(iconDataStream); - image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High); + var image = Image.Load(iconDataStream); + image.Mutate(x => x.Resize(128, 128)); SaveBitmapAsIcon(image, iconPath); - var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0); + var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath), iconPath, 0); shortcut.StringData.NameString = cleanedAppName; shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk")); } [SupportedOSPlatform("linux")] - private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName) + private static void CreateShortcutLinux(string applicationFilePath, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName) { string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh"); var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop"); iconPath += ".png"; - var image = SKBitmap.Decode(iconData); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - using var file = File.OpenWrite(iconPath); - data.SaveTo(file); + var image = Image.Load(iconData); + image.SaveAsPng(iconPath); using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop")); - outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}"); + outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath)}"); } [SupportedOSPlatform("macos")] - private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName) + private static void CreateShortcutMacos(string appFilePath, byte[] iconData, string desktopPath, string cleanedAppName) { string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"); var plistFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.plist"); @@ -63,7 +64,7 @@ namespace Ryujinx.UI.Common.Helper string scriptPath = Path.Combine(scriptFolderPath, ScriptName); using StreamWriter scriptFile = new(scriptPath); - scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId)); + scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath)); // Set execute permission FileInfo fileInfo = new(scriptPath); @@ -77,10 +78,8 @@ namespace Ryujinx.UI.Common.Helper } const string IconName = "icon.png"; - 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); + var image = Image.Load(iconData); + image.SaveAsPng(Path.Combine(resourceFolderPath, IconName)); // plist file using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist")); @@ -96,7 +95,7 @@ namespace Ryujinx.UI.Common.Helper { string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app"); - CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath); + CreateShortcutWindows(applicationFilePath, iconData, iconPath, cleanedAppName, desktopPath); return; } @@ -106,14 +105,14 @@ namespace Ryujinx.UI.Common.Helper string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx"); Directory.CreateDirectory(iconPath); - CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName); + CreateShortcutLinux(applicationFilePath, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName); return; } if (OperatingSystem.IsMacOS()) { - CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName); + CreateShortcutMacos(applicationFilePath, iconData, desktopPath, cleanedAppName); return; } @@ -121,7 +120,7 @@ namespace Ryujinx.UI.Common.Helper throw new NotImplementedException("Shortcut support has not been implemented yet for this OS."); } - private static string GetArgsString(string appFilePath, string applicationId) + private static string GetArgsString(string appFilePath) { // args are first defined as a list, for easier adjustments in the future var argsList = new List(); @@ -132,12 +131,6 @@ namespace Ryujinx.UI.Common.Helper argsList.Add($"\"{CommandLineState.BaseDirPathArg}\""); } - if (appFilePath.ToLower().EndsWith(".xci")) - { - argsList.Add("--application-id"); - argsList.Add($"\"{applicationId}\""); - } - argsList.Add($"\"{appFilePath}\""); return String.Join(" ", argsList); @@ -149,7 +142,7 @@ namespace Ryujinx.UI.Common.Helper /// The source bitmap image that will be saved as an .ico file /// The location that the new .ico file will be saved too (Make sure to include '.ico' in the path). [SupportedOSPlatform("windows")] - private static void SaveBitmapAsIcon(SKBitmap source, string filePath) + private static void SaveBitmapAsIcon(Image 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 }; @@ -157,16 +150,13 @@ namespace Ryujinx.UI.Common.Helper fs.Write(header); // Writing actual data - using var data = source.Encode(SKEncodedImageFormat.Png, 100); - data.SaveTo(fs); + source.Save(fs, PngFormat.Instance); // 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)); } } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index f4bfd1169..d405f3205 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -40,17 +40,20 @@ using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; using Silk.NET.Vulkan; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SPB.Graphics.Vulkan; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; +using Image = SixLabors.ImageSharp.Image; using InputManager = Ryujinx.Input.HLE.InputManager; using IRenderer = Ryujinx.Graphics.GAL.IRenderer; using Key = Ryujinx.Input.Key; @@ -132,14 +135,12 @@ namespace Ryujinx.Ava public int Width { get; private set; } public int Height { get; private set; } public string ApplicationPath { get; private set; } - public ulong ApplicationId { get; private set; } public bool ScreenshotRequested { get; set; } public AppHost( RendererHost renderer, InputManager inputManager, string applicationPath, - ulong applicationId, VirtualFileSystem virtualFileSystem, ContentManager contentManager, AccountManager accountManager, @@ -163,7 +164,6 @@ namespace Ryujinx.Ava NpadManager = _inputManager.CreateNpadManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager(); ApplicationPath = applicationPath; - ApplicationId = applicationId; VirtualFileSystem = virtualFileSystem; ContentManager = contentManager; @@ -366,25 +366,25 @@ namespace Ryujinx.Ava return; } - var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; - using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); + Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) + : Image.LoadPixelData(e.Data, e.Width, e.Height); - Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length); + if (e.FlipX) + { + image.Mutate(x => x.Flip(FlipMode.Horizontal)); + } - using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height); - using SKCanvas canvas = new SKCanvas(bitmapToSave); + if (e.FlipY) + { + image.Mutate(x => x.Flip(FlipMode.Vertical)); + } - canvas.Clear(SKColors.Black); + image.SaveAsPng(path, new PngEncoder + { + ColorType = PngColorType.Rgb, + }); - float scaleX = e.FlipX ? -1 : 1; - float scaleY = e.FlipY ? -1 : 1; - - var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f); - - canvas.SetMatrix(matrix); - canvas.DrawBitmap(bitmap, SKPoint.Empty); - - SaveBitmapAsPng(bitmapToSave, path); + image.Dispose(); Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); } @@ -396,14 +396,6 @@ namespace Ryujinx.Ava } } - private void SaveBitmapAsPng(SKBitmap bitmap, string path) - { - using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); - using var stream = File.OpenWrite(path); - - data.SaveTo(stream); - } - public void Start() { if (OperatingSystem.IsWindows()) @@ -714,7 +706,7 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - if (!Device.LoadXci(ApplicationPath, ApplicationId)) + if (!Device.LoadXci(ApplicationPath)) { Device.Dispose(); @@ -741,7 +733,7 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - if (!Device.LoadNsp(ApplicationPath, ApplicationId)) + if (!Device.LoadNsp(ApplicationPath)) { Device.Dispose(); @@ -845,7 +837,7 @@ namespace Ryujinx.Ava Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); // Initialize Configuration. - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB; + var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; HLEConfiguration configuration = new(VirtualFileSystem, _viewModel.LibHacHorizonManager, diff --git a/src/Ryujinx/Assets/Locales/ar_SA.json b/src/Ryujinx/Assets/Locales/ar_SA.json index 73e55633b..197d5531f 100644 --- a/src/Ryujinx/Assets/Locales/ar_SA.json +++ b/src/Ryujinx/Assets/Locales/ar_SA.json @@ -1,5 +1,5 @@ { - "Language": "اَلْعَرَبِيَّةُ", + "Language": "العربية", "MenuBarFileOpenApplet": "فتح التطبيق المصغر", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "‫افتح تطبيق تحرير Mii في الوضع المستقل", "SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة", @@ -10,20 +10,21 @@ "SettingsTabSystemUseHypervisor": "استخدم مراقب الأجهزة الافتراضية", "MenuBarFile": "_ملف", "MenuBarFileOpenFromFile": "_تحميل تطبيق من ملف", + "MenuBarFileOpenFromFileError": "لم يتم العثور على تطبيقات في الملف المحدد.", "MenuBarFileOpenUnpacked": "تحميل لُعْبَة غير محزومة", - "MenuBarFileOpenEmuFolder": "‫فتح مجلد Ryujinx", + "MenuBarFileOpenEmuFolder": "‫فتح مجلد ريوجينكس", "MenuBarFileOpenLogsFolder": "فتح مجلد السجلات", "MenuBarFileExit": "_خروج", "MenuBarOptions": "_خيارات", "MenuBarOptionsToggleFullscreen": "التبديل إلى وضع ملء الشاشة", "MenuBarOptionsStartGamesInFullscreen": "ابدأ الألعاب في وضع ملء الشاشة", "MenuBarOptionsStopEmulation": "إيقاف المحاكاة", - "MenuBarOptionsSettings": "_الإعدادات", + "MenuBarOptionsSettings": "_إعدادات", "MenuBarOptionsManageUserProfiles": "_إدارة الملفات الشخصية للمستخدم", - "MenuBarActions": "_الإجراءات", + "MenuBarActions": "_أجراءات", "MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ", "MenuBarActionsScanAmiibo": "‫فحص Amiibo", - "MenuBarTools": "_الأدوات", + "MenuBarTools": "_أدوات", "MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت", "MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP", "MenuBarFileToolsInstallFirmwareFromDirectory": "تثبيت برنامج ثابت من مجلد", @@ -39,7 +40,7 @@ "MenuBarHelpAbout": "حول", "MenuSearch": "بحث...", "GameListHeaderFavorite": "مفضلة", - "GameListHeaderIcon": "الأيقونة", + "GameListHeaderIcon": "أيقونة", "GameListHeaderApplication": "الاسم", "GameListHeaderDeveloper": "المطور", "GameListHeaderVersion": "الإصدار", @@ -144,10 +145,10 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "هاكات", "SettingsTabSystemHacksNote": "قد يتسبب في عدم الاستقرار", - "SettingsTabSystemExpandDramSize": "استخدام تخطيط الذاكرة البديل (المطورين)", + "SettingsTabSystemExpandDramSize": "توسيع DRAM إلى 8GiB", "SettingsTabSystemIgnoreMissingServices": "تجاهل الخدمات المفقودة", "SettingsTabGraphics": "الرسومات", - "SettingsTabGraphicsAPI": "API الرسومات ", + "SettingsTabGraphicsAPI": "برمجة تطبيقات الرسومات", "SettingsTabGraphicsEnableShaderCache": "تفعيل ذاكرة المظللات المؤقتة", "SettingsTabGraphicsAnisotropicFiltering": "تصفية:", "SettingsTabGraphicsAnisotropicFilteringAuto": "تلقائي", @@ -291,9 +292,9 @@ "KeyDown": "اسفل", "KeyLeft": "يسار", "KeyRight": "يمين", - "KeyEnter": "مفتاح الإدخال", + "KeyEnter": "زر Enter", "KeyEscape": "زر ‫Escape", - "KeySpace": "مسافة", + "KeySpace": "زر المسافة", "KeyTab": "زر ‫Tab", "KeyBackSpace": "زر المسح للخلف", "KeyInsert": "زر Insert", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "تعيين كمفضل", "GameListContextMenuToggleFavoriteToolTip": "تبديل الحالة المفضلة للعبة", "SettingsTabGeneralTheme": "السمة:", + "SettingsTabGeneralThemeAuto": "تلقائي", "SettingsTabGeneralThemeDark": "داكن", "SettingsTabGeneralThemeLight": "فاتح", "ControllerSettingsConfigureGeneral": "ضبط", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "تعيين الذاكرة مباشرة في مساحة عنوان المضيف. تجميع وتنفيذ JIT أسرع بكثير.", "MemoryManagerUnsafeTooltip": "تعيين الذاكرة مباشرة، ولكن لا تخفي العنوان داخل مساحة عنوان الضيف قبل الوصول. أسرع، ولكن على حساب السلامة. يمكن لتطبيق الضيف الوصول إلى الذاكرة من أي مكان في ريوجينكس، لذا قم بتشغيل البرامج التي تثق بها فقط مع هذا الوضع.", "UseHypervisorTooltip": "استخدم هايبرڤايزور بدلا من JIT. يعمل على تحسين الأداء بشكل كبير عند توفره، ولكنه قد يكون غير مستقر في حالته الحالية.", - "DRamTooltip": "يستخدم تخطيط وضع الذاكرة البديل لتقليد نموذج سويتش المطورين.\n\nيعد هذا مفيدا فقط لحزم النسيج عالية الدقة أو تعديلات دقة 4K. لا يحسن الأداء.\n\nاتركه معطلا إذا لم تكن متأكدا.", + "DRamTooltip": "يستخدم تخطيط وضع الذاكرة البديل لتقليد نموذج Switch المطورين.\n\nيعد هذا مفيدا فقط لحزم النسيج عالية الدقة أو تعديلات دقة 4K. لا يحسن الأداء.\n\nاتركه معطلا إذا لم تكن متأكدا.", "IgnoreMissingServicesTooltip": "يتجاهل خدمات نظام هوريزون غير المنفذة. قد يساعد هذا في تجاوز الأعطال عند تشغيل ألعاب معينة.\n\nاتركه معطلا إذا كنت غير متأكد.", "GraphicsBackendThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.", "GalThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "فتح دليل الإعداد", "NoUpdate": "لا يوجد تحديث", "TitleUpdateVersionLabel": "الإصدار: {0}", + "TitleBundledUpdateVersionLabel": "مجمع: الإصدار {0}", + "TitleBundledDlcLabel": "مجمع:", "RyujinxInfo": "ريوجينكس - معلومات", "RyujinxConfirm": "ريوجينكس - تأكيد", "FileDialogAllTypes": "كل الأنواع", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.", "GraphicsAALabel": "تنعيم الحواف:", "GraphicsScalingFilterLabel": "فلتر التكبير:", - "GraphicsScalingFilterTooltip": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.", + "GraphicsScalingFilterTooltip": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nArea scaling موصى لخفض الدقات الكبيرة عن حجم النافذة و يمكن استخدمه لتحقيق مضاد الحواف فائق العينة عند التخفيض بمقدار اكثر من x2.\n\nاتركه على Bilinear إذا لم تكن متأكدا.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "المستوى", "GraphicsScalingFilterLevelTooltip": "اضبط مستوى وضوح FSR 1.0. الأعلى هو أكثر وضوحا.", "SmaaLow": "SMAA منخفض", diff --git a/src/Ryujinx/Assets/Locales/cs_CZ.json b/src/Ryujinx/Assets/Locales/cs_CZ.json new file mode 100644 index 000000000..9c251ca72 --- /dev/null +++ b/src/Ryujinx/Assets/Locales/cs_CZ.json @@ -0,0 +1,785 @@ +{ + "Language": "Čeština", + "MenuBarFileOpenApplet": "Otevřít Applet", + "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otevřít Applet editoru Mii v samostatném režimu", + "SettingsTabInputDirectMouseAccess": "Přístup přímo myší", + "SettingsTabSystemMemoryManagerMode": "Režim správy paměti:", + "SettingsTabSystemMemoryManagerModeSoftware": "Softwarový", + "SettingsTabSystemMemoryManagerModeHost": "Hostitel (rychlý)", + "SettingsTabSystemMemoryManagerModeHostUnchecked": "Nekontrolovaný hostitel (nejrychlejší, riskantní)", + "SettingsTabSystemUseHypervisor": "Použít Hypervizor", + "MenuBarFile": "_Soubor", + "MenuBarFileOpenFromFile": "_Načíst aplikaci ze souboru", + "MenuBarFileOpenFromFileError": "Ve vybraném souboru nebyly nalezeny žádné aplikace.", + "MenuBarFileOpenUnpacked": "Načíst _rozbalenou hru", + "MenuBarFileOpenEmuFolder": "Otevřít složku Ryujinx", + "MenuBarFileOpenLogsFolder": "Otevřít složku s logy", + "MenuBarFileExit": "_Ukončit", + "MenuBarOptions": "_Možnosti", + "MenuBarOptionsToggleFullscreen": "Režim celé obrazovky", + "MenuBarOptionsStartGamesInFullscreen": "Spuštět hry v režimu celé obrazovky", + "MenuBarOptionsStopEmulation": "Ukončit emulaci", + "MenuBarOptionsSettings": "_Nastavení", + "MenuBarOptionsManageUserProfiles": "_Spravovat uživatelské profily", + "MenuBarActions": "_Akce", + "MenuBarOptionsSimulateWakeUpMessage": "Simulovat zprávu probuzení", + "MenuBarActionsScanAmiibo": "Naskenovat Amiibo", + "MenuBarTools": "_Nástroje", + "MenuBarToolsInstallFirmware": "Nainstalovat firmware", + "MenuBarFileToolsInstallFirmwareFromFile": "Nainstalovat firmware z XCI nebo ZIP", + "MenuBarFileToolsInstallFirmwareFromDirectory": "Nainstalovat firmware z adresáře", + "MenuBarToolsManageFileTypes": "Spravovat typy souborů", + "MenuBarToolsInstallFileTypes": "Nainstalovat typy souborů", + "MenuBarToolsUninstallFileTypes": "Odinstalovat typy souborů", + "MenuBarView": "_Zobrazit", + "MenuBarViewWindow": "Velikost okna", + "MenuBarViewWindow720": "720p", + "MenuBarViewWindow1080": "1080p", + "MenuBarHelp": "_Nápověda", + "MenuBarHelpCheckForUpdates": "Zkontrolovat aktualizace", + "MenuBarHelpAbout": "O aplikaci", + "MenuSearch": "Hledat...", + "GameListHeaderFavorite": "Oblíbené", + "GameListHeaderIcon": "Ikona", + "GameListHeaderApplication": "Název", + "GameListHeaderDeveloper": "Vývojář", + "GameListHeaderVersion": "Verze", + "GameListHeaderTimePlayed": "Doba hraní", + "GameListHeaderLastPlayed": "Naposledy hráno", + "GameListHeaderFileExtension": "Přípona souboru", + "GameListHeaderFileSize": "Velikost souboru", + "GameListHeaderPath": "Cesta", + "GameListContextMenuOpenUserSaveDirectory": "Otevřít adresář s uloženými daty uživatelů", + "GameListContextMenuOpenUserSaveDirectoryToolTip": "Otevře adresář, který obsahuje uložená data uživatele pro aplikaci", + "GameListContextMenuOpenDeviceSaveDirectory": "Otevřít adresář s uloženými daty zařízení", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Otevře adresář, který obsahuje uložená data zařízení pro aplikaci", + "GameListContextMenuOpenBcatSaveDirectory": "Otevřít adresář s uloženými daty BCAT", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Otevře adresář, který obsahuje uložená data BCAT pro aplikaci", + "GameListContextMenuManageTitleUpdates": "Spravovat aktualizace titulu", + "GameListContextMenuManageTitleUpdatesToolTip": "Otevře okno správy aktualizací titulu", + "GameListContextMenuManageDlc": "Spravovat DLC", + "GameListContextMenuManageDlcToolTip": "Otevře okno správy DLC titulu", + "GameListContextMenuCacheManagement": "Správa mezipaměti", + "GameListContextMenuCacheManagementPurgePptc": "Přidat do fronty obnovu PPTC", + "GameListContextMenuCacheManagementPurgePptcToolTip": "Vyvolá obnovení PPTC po startu při příštím spuštění hry", + "GameListContextMenuCacheManagementPurgeShaderCache": "Smazat mezipaměť shaderu", + "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Odstraní mezipaměť shaderu aplikace", + "GameListContextMenuCacheManagementOpenPptcDirectory": "Otevřít adresář PPTC", + "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Otevře adresář, který obsahuje PPTC mezipaměť aplikace", + "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Otevřít adresář mezipaměti shaderu", + "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Otevře adresář, který obsahuje mezipaměť shaderu aplikace", + "GameListContextMenuExtractData": "Extrahovat data", + "GameListContextMenuExtractDataExeFS": "ExeFS", + "GameListContextMenuExtractDataExeFSToolTip": "Extrahovat sekci ExeFS z aktuální konfigurace aplikace (včetně aktualizací)", + "GameListContextMenuExtractDataRomFS": "RomFS", + "GameListContextMenuExtractDataRomFSToolTip": "Extrahovat sekci RomFS z aktuální konfigurace aplikace (včetně aktualizací)", + "GameListContextMenuExtractDataLogo": "Logo", + "GameListContextMenuExtractDataLogoToolTip": "Extrahovat sekci loga z aktuální konfigurace aplikace (včetně aktualizací)", + "GameListContextMenuCreateShortcut": "Vytvořit zástupce aplikace", + "GameListContextMenuCreateShortcutToolTip": "Vytvořit zástupce na ploše, který spustí vybranou aplikaci", + "GameListContextMenuCreateShortcutToolTipMacOS": "Vytvořit zástupce v macOS složce Aplikace, která spustí vybranou aplikaci", + "GameListContextMenuOpenModsDirectory": "Otevřít adresář módů", + "GameListContextMenuOpenModsDirectoryToolTip": "Otevře adresář, který obsahuje módy aplikace", + "GameListContextMenuOpenSdModsDirectory": "Otevřít adresář módů Atmosphere", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Otevře alternativní adresář Atmosphere na SD kartě, který obsahuje módy aplikace. Užitečné pro módy, které jsou zabaleny pro skutečný hardware.", + "StatusBarGamesLoaded": "{0}/{1} her načteno", + "StatusBarSystemVersion": "Verze systému: {0}", + "LinuxVmMaxMapCountDialogTitle": "Detekován nízký limit pro mapování paměti", + "LinuxVmMaxMapCountDialogTextPrimary": "Chcete zvýšit hodnotu vm.max_map_count na {0}", + "LinuxVmMaxMapCountDialogTextSecondary": "Některé hry se mohou pokusit vytvořit více mapování paměti, než je aktuálně povoleno. Ryujinx crashne, jakmile bude tento limit překročen.", + "LinuxVmMaxMapCountDialogButtonUntilRestart": "Ano, do dalšího restartu", + "LinuxVmMaxMapCountDialogButtonPersistent": "Ano, natrvalo", + "LinuxVmMaxMapCountWarningTextPrimary": "Maximální počet mapování paměti je nižší, než je doporučeno.", + "LinuxVmMaxMapCountWarningTextSecondary": "Aktuální hodnota vm.max_map_count ({0}) je nižší než {1}. Některé hry se mohou pokusit vytvořit více mapování paměti, než je aktuálně povoleno. Ryujinx crashne, jakmile bude tento limit překročen.\n\nMožná budete chtít buď ručně zvýšit limit nebo nainstalovat pkexec, který umožňuje Ryujinxu vám s tím pomoci.", + "Settings": "Nastavení", + "SettingsTabGeneral": "Uživatelské rozhraní", + "SettingsTabGeneralGeneral": "Obecné", + "SettingsTabGeneralEnableDiscordRichPresence": "Povolit Discord Rich Presence", + "SettingsTabGeneralCheckUpdatesOnLaunch": "Zkontrolovat aktualizace při spuštění", + "SettingsTabGeneralShowConfirmExitDialog": "Zobrazovat dialog s potvrzením ukončení", + "SettingsTabGeneralRememberWindowState": "Pamatovat si velikost/pozici okna", + "SettingsTabGeneralHideCursor": "Skrýt kurzor:", + "SettingsTabGeneralHideCursorNever": "Nikdy", + "SettingsTabGeneralHideCursorOnIdle": "Při nečinnosti", + "SettingsTabGeneralHideCursorAlways": "Vždy", + "SettingsTabGeneralGameDirectories": "Adresáře s hrami", + "SettingsTabGeneralAdd": "Přidat", + "SettingsTabGeneralRemove": "Odebrat", + "SettingsTabSystem": "Systém", + "SettingsTabSystemCore": "Hlavní", + "SettingsTabSystemSystemRegion": "Region systému:", + "SettingsTabSystemSystemRegionJapan": "Japonsko", + "SettingsTabSystemSystemRegionUSA": "USA", + "SettingsTabSystemSystemRegionEurope": "Evropa", + "SettingsTabSystemSystemRegionAustralia": "Austrálie", + "SettingsTabSystemSystemRegionChina": "Čína", + "SettingsTabSystemSystemRegionKorea": "Korea", + "SettingsTabSystemSystemRegionTaiwan": "Taiwan", + "SettingsTabSystemSystemLanguage": "Jazyk systému:", + "SettingsTabSystemSystemLanguageJapanese": "Japonština", + "SettingsTabSystemSystemLanguageAmericanEnglish": "Americká angličtina", + "SettingsTabSystemSystemLanguageFrench": "Francouzština", + "SettingsTabSystemSystemLanguageGerman": "Němčina", + "SettingsTabSystemSystemLanguageItalian": "Italština", + "SettingsTabSystemSystemLanguageSpanish": "Španělština", + "SettingsTabSystemSystemLanguageChinese": "Čínština", + "SettingsTabSystemSystemLanguageKorean": "Korejština", + "SettingsTabSystemSystemLanguageDutch": "Nizozemština", + "SettingsTabSystemSystemLanguagePortuguese": "Portugalština", + "SettingsTabSystemSystemLanguageRussian": "Ruština", + "SettingsTabSystemSystemLanguageTaiwanese": "Tchajwanština", + "SettingsTabSystemSystemLanguageBritishEnglish": "Britská angličtina", + "SettingsTabSystemSystemLanguageCanadianFrench": "Kanadská francouzština", + "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latinskoamerická španělština", + "SettingsTabSystemSystemLanguageSimplifiedChinese": "Zjednodušená čínština", + "SettingsTabSystemSystemLanguageTraditionalChinese": "Tradiční čínština", + "SettingsTabSystemSystemTimeZone": "Časová zóna systému:", + "SettingsTabSystemSystemTime": "Čas systému:", + "SettingsTabSystemEnableVsync": "VSync", + "SettingsTabSystemEnablePptc": "PPTC (Profilovaná mezipaměť trvalého překladu)", + "SettingsTabSystemEnableFsIntegrityChecks": "Kontroly integrity souborového systému", + "SettingsTabSystemAudioBackend": "Backend zvuku:", + "SettingsTabSystemAudioBackendDummy": "Fiktivní", + "SettingsTabSystemAudioBackendOpenAL": "OpenAL", + "SettingsTabSystemAudioBackendSoundIO": "SoundIO", + "SettingsTabSystemAudioBackendSDL2": "SDL2", + "SettingsTabSystemHacks": "Hacky", + "SettingsTabSystemHacksNote": "Mohou způsobovat nestabilitu", + "SettingsTabSystemExpandDramSize": "Rozšířit DRAM na 8GiB", + "SettingsTabSystemIgnoreMissingServices": "Ignorovat chybějící služby", + "SettingsTabGraphics": "Grafika", + "SettingsTabGraphicsAPI": "Grafické API", + "SettingsTabGraphicsEnableShaderCache": "Povolit mezipaměť shaderu", + "SettingsTabGraphicsAnisotropicFiltering": "Anizotropní filtrování:", + "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", + "SettingsTabGraphicsAnisotropicFiltering2x": "2x", + "SettingsTabGraphicsAnisotropicFiltering4x": "4x", + "SettingsTabGraphicsAnisotropicFiltering8x": "8x", + "SettingsTabGraphicsAnisotropicFiltering16x": "16x", + "SettingsTabGraphicsResolutionScale": "Měřítko rozlišení:", + "SettingsTabGraphicsResolutionScaleCustom": "Vlastní (Nedoporučeno)", + "SettingsTabGraphicsResolutionScaleNative": "Nativní (720p/1080p)", + "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", + "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", + "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Nedoporučeno)", + "SettingsTabGraphicsAspectRatio": "Poměr stran:", + "SettingsTabGraphicsAspectRatio4x3": "4:3", + "SettingsTabGraphicsAspectRatio16x9": "16:9", + "SettingsTabGraphicsAspectRatio16x10": "16:10", + "SettingsTabGraphicsAspectRatio21x9": "21:9", + "SettingsTabGraphicsAspectRatio32x9": "32:9", + "SettingsTabGraphicsAspectRatioStretch": "Roztáhnout do okna", + "SettingsTabGraphicsDeveloperOptions": "Možnosti pro vývojáře", + "SettingsTabGraphicsShaderDumpPath": "Cesta pro dumpování grafických shaderů", + "SettingsTabLogging": "Logování", + "SettingsTabLoggingLogging": "Logování", + "SettingsTabLoggingEnableLoggingToFile": "Povolit logování do souboru", + "SettingsTabLoggingEnableStubLogs": "Povolit stub logy", + "SettingsTabLoggingEnableInfoLogs": "Povolit informační logy", + "SettingsTabLoggingEnableWarningLogs": "Povolit logy varování", + "SettingsTabLoggingEnableErrorLogs": "Povolit logy chyb", + "SettingsTabLoggingEnableTraceLogs": "Povolit trasovací logy", + "SettingsTabLoggingEnableGuestLogs": "Povolit logy hosta", + "SettingsTabLoggingEnableFsAccessLogs": "Povolit logy přístupů k souborovému systému", + "SettingsTabLoggingFsGlobalAccessLogMode": "Režim logování globálních přístupů k souborovému systému", + "SettingsTabLoggingDeveloperOptions": "Možnosti pro vývojáře", + "SettingsTabLoggingDeveloperOptionsNote": "VAROVÁNÍ: Sníží výkon", + "SettingsTabLoggingGraphicsBackendLogLevel": "Úroveň logování grafického backendu:", + "SettingsTabLoggingGraphicsBackendLogLevelNone": "Žádné", + "SettingsTabLoggingGraphicsBackendLogLevelError": "Chyby", + "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Zpomalení", + "SettingsTabLoggingGraphicsBackendLogLevelAll": "Vše", + "SettingsTabLoggingEnableDebugLogs": "Povolit logy pro ladění", + "SettingsTabInput": "Vstup", + "SettingsTabInputEnableDockedMode": "Dokovaný režim", + "SettingsTabInputDirectKeyboardAccess": "Přístup přímo klávesnicí", + "SettingsButtonSave": "Uložit", + "SettingsButtonClose": "Zavřít", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Zrušit", + "SettingsButtonApply": "Použít", + "ControllerSettingsPlayer": "Hráč", + "ControllerSettingsPlayer1": "Hráč 1", + "ControllerSettingsPlayer2": "Hráč 2", + "ControllerSettingsPlayer3": "Hráč 3", + "ControllerSettingsPlayer4": "Hráč 4", + "ControllerSettingsPlayer5": "Hráč 5", + "ControllerSettingsPlayer6": "Hráč 6", + "ControllerSettingsPlayer7": "Hráč 7", + "ControllerSettingsPlayer8": "Hráč 8", + "ControllerSettingsHandheld": "Handheld", + "ControllerSettingsInputDevice": "Vstupní zařízení", + "ControllerSettingsRefresh": "Obnovit", + "ControllerSettingsDeviceDisabled": "Deaktivováno", + "ControllerSettingsControllerType": "Typ ovladače", + "ControllerSettingsControllerTypeHandheld": "Handheld", + "ControllerSettingsControllerTypeProController": "Pro Controller", + "ControllerSettingsControllerTypeJoyConPair": "Pár JoyConů", + "ControllerSettingsControllerTypeJoyConLeft": "Levý JoyCon", + "ControllerSettingsControllerTypeJoyConRight": "Pravý JoyCon", + "ControllerSettingsProfile": "Profil", + "ControllerSettingsProfileDefault": "Výchozí", + "ControllerSettingsLoad": "Načíst", + "ControllerSettingsAdd": "Přidat", + "ControllerSettingsRemove": "Odebrat", + "ControllerSettingsButtons": "Tlačítka", + "ControllerSettingsButtonA": "A", + "ControllerSettingsButtonB": "B", + "ControllerSettingsButtonX": "X", + "ControllerSettingsButtonY": "Y", + "ControllerSettingsButtonPlus": "+", + "ControllerSettingsButtonMinus": "-", + "ControllerSettingsDPad": "Směrový kříž", + "ControllerSettingsDPadUp": "Nahoru", + "ControllerSettingsDPadDown": "Dolů", + "ControllerSettingsDPadLeft": "Doleva", + "ControllerSettingsDPadRight": "Doprava", + "ControllerSettingsStickButton": "Tlačítko", + "ControllerSettingsStickUp": "Nahoru", + "ControllerSettingsStickDown": "Dolů", + "ControllerSettingsStickLeft": "Doleva", + "ControllerSettingsStickRight": "Doprava", + "ControllerSettingsStickStick": "Páčka", + "ControllerSettingsStickInvertXAxis": "Invertovat X souřadnici páčky", + "ControllerSettingsStickInvertYAxis": "Invertovat Y souřadnici páčky", + "ControllerSettingsStickDeadzone": "Mrtvá zóna:", + "ControllerSettingsLStick": "Levá páčka", + "ControllerSettingsRStick": "Pravá páčka", + "ControllerSettingsTriggersLeft": "Spouště vlevo", + "ControllerSettingsTriggersRight": "Spouště vpravo", + "ControllerSettingsTriggersButtonsLeft": "Tlačítka spouště vlevo", + "ControllerSettingsTriggersButtonsRight": "Tlačítka spouště vpravo", + "ControllerSettingsTriggers": "Spouště", + "ControllerSettingsTriggerL": "L", + "ControllerSettingsTriggerR": "R", + "ControllerSettingsTriggerZL": "ZL", + "ControllerSettingsTriggerZR": "ZR", + "ControllerSettingsLeftSL": "SL", + "ControllerSettingsLeftSR": "SR", + "ControllerSettingsRightSL": "SL", + "ControllerSettingsRightSR": "SR", + "ControllerSettingsExtraButtonsLeft": "Tlačítka vlevo", + "ControllerSettingsExtraButtonsRight": "Tlačítka vpravo", + "ControllerSettingsMisc": "Ostatní", + "ControllerSettingsTriggerThreshold": "Práh spouště:", + "ControllerSettingsMotion": "Pohyb", + "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Použít pohyb kompatibilní s CemuHook", + "ControllerSettingsMotionControllerSlot": "Slot ovladače:", + "ControllerSettingsMotionMirrorInput": "Zrcadlit vstup", + "ControllerSettingsMotionRightJoyConSlot": "Slot pravého JoyConu:", + "ControllerSettingsMotionServerHost": "Server hostitele:", + "ControllerSettingsMotionGyroSensitivity": "Citlivost gyroskopu:", + "ControllerSettingsMotionGyroDeadzone": "Mrtvá zóna gyroskopu:", + "ControllerSettingsSave": "Uložit", + "ControllerSettingsClose": "Zavřít", + "KeyUnknown": "Neznámé", + "KeyShiftLeft": "Levý Shift", + "KeyShiftRight": "Pravý Shift", + "KeyControlLeft": "Levý Ctrl", + "KeyMacControlLeft": "Levý ⌃", + "KeyControlRight": "Pravý Ctrl", + "KeyMacControlRight": "Pravý ⌃", + "KeyAltLeft": "Levý Alt", + "KeyMacAltLeft": "Levý ⌥", + "KeyAltRight": "Pravý Alt", + "KeyMacAltRight": "Pravý ⌥", + "KeyWinLeft": "Levý ⊞", + "KeyMacWinLeft": "Levý ⌘", + "KeyWinRight": "Pravý ⊞", + "KeyMacWinRight": "Pravý ⌘", + "KeyMenu": "Menu", + "KeyUp": "Nahoru", + "KeyDown": "Dolů", + "KeyLeft": "Doleva", + "KeyRight": "Doprava", + "KeyEnter": "Enter", + "KeyEscape": "Escape", + "KeySpace": "Mezerník", + "KeyTab": "Tab", + "KeyBackSpace": "Backspace", + "KeyInsert": "Insert", + "KeyDelete": "Delete", + "KeyPageUp": "Page Up", + "KeyPageDown": "Page Down", + "KeyHome": "Home", + "KeyEnd": "End", + "KeyCapsLock": "Caps Lock", + "KeyScrollLock": "Scroll Lock", + "KeyPrintScreen": "Print Screen", + "KeyPause": "Pause", + "KeyNumLock": "Num Lock", + "KeyClear": "Clear", + "KeyKeypad0": "Numerická 0", + "KeyKeypad1": "Numerická 1", + "KeyKeypad2": "Numerická 2", + "KeyKeypad3": "Numerická 3", + "KeyKeypad4": "Numerická 4", + "KeyKeypad5": "Numerická 5", + "KeyKeypad6": "Numerická 6", + "KeyKeypad7": "Numerická 7", + "KeyKeypad8": "Numerická 8", + "KeyKeypad9": "Numerická 9", + "KeyKeypadDivide": "Numerické dělení", + "KeyKeypadMultiply": "Numerické násobení", + "KeyKeypadSubtract": "Numerické minus", + "KeyKeypadAdd": "Numerické plus", + "KeyKeypadDecimal": "Numerická tečka", + "KeyKeypadEnter": "Numerický enter", + "KeyNumber0": "0", + "KeyNumber1": "1", + "KeyNumber2": "2", + "KeyNumber3": "3", + "KeyNumber4": "4", + "KeyNumber5": "5", + "KeyNumber6": "6", + "KeyNumber7": "7", + "KeyNumber8": "8", + "KeyNumber9": "9", + "KeyTilde": "~", + "KeyGrave": "`", + "KeyMinus": "-", + "KeyPlus": "+", + "KeyBracketLeft": "[", + "KeyBracketRight": "]", + "KeySemicolon": ";", + "KeyQuote": "\"", + "KeyComma": ",", + "KeyPeriod": ".", + "KeySlash": "/", + "KeyBackSlash": "\\", + "KeyUnbound": "Nenastaveno", + "GamepadLeftStick": "Tlačítko levé páčky", + "GamepadRightStick": "Tlačítko pravé páčky", + "GamepadLeftShoulder": "Levé tlačítko", + "GamepadRightShoulder": "Pravé tlačítko", + "GamepadLeftTrigger": "Levá spoušť", + "GamepadRightTrigger": "Pravá spoušť", + "GamepadDpadUp": "Nahoru", + "GamepadDpadDown": "Dolů", + "GamepadDpadLeft": "Doleva", + "GamepadDpadRight": "Doprava", + "GamepadMinus": "-", + "GamepadPlus": "+", + "GamepadGuide": "Guide", + "GamepadMisc1": "Ostatní", + "GamepadPaddle1": "Páčka 1", + "GamepadPaddle2": "Páčka 2", + "GamepadPaddle3": "Páčka 3", + "GamepadPaddle4": "Páčka 4", + "GamepadTouchpad": "Touchpad", + "GamepadSingleLeftTrigger0": "Levá spoušť 0", + "GamepadSingleRightTrigger0": "Pravá spoušť 0", + "GamepadSingleLeftTrigger1": "Levá spoušť 1", + "GamepadSingleRightTrigger1": "Pravá spoušť 1", + "StickLeft": "Levá páčka", + "StickRight": "Pravá páčka", + "UserProfilesSelectedUserProfile": "Vybraný uživatelský profil:", + "UserProfilesSaveProfileName": "Uložit název profilu", + "UserProfilesChangeProfileImage": "Změnit profilový obrázek", + "UserProfilesAvailableUserProfiles": "Dostupné uživatelské profily:", + "UserProfilesAddNewProfile": "Vytvořit profil", + "UserProfilesDelete": "Smazat", + "UserProfilesClose": "Zavřít", + "ProfileNameSelectionWatermark": "Zvolte si přezdívku", + "ProfileImageSelectionTitle": "Výběr profilového obrázku", + "ProfileImageSelectionHeader": "Zvolte profilový obrázek", + "ProfileImageSelectionNote": "Můžete importovat vlastní profilový obrázek, nebo vybrat avatar z firmwaru systému", + "ProfileImageSelectionImportImage": "Importovat soubor obrázku", + "ProfileImageSelectionSelectAvatar": "Vybrat avatar z firmwaru", + "InputDialogTitle": "Dialogové okno vstupu", + "InputDialogOk": "OK", + "InputDialogCancel": "Zrušit", + "InputDialogAddNewProfileTitle": "Zvolte název profilu", + "InputDialogAddNewProfileHeader": "Zadejte prosím název profilu", + "InputDialogAddNewProfileSubtext": "(Maximální délka: {0})", + "AvatarChoose": "Vybrat avatar", + "AvatarSetBackgroundColor": "Nastavit barvu pozadí", + "AvatarClose": "Zavřít", + "ControllerSettingsLoadProfileToolTip": "Načíst profil", + "ControllerSettingsAddProfileToolTip": "Přidat profil", + "ControllerSettingsRemoveProfileToolTip": "Odebrat profil", + "ControllerSettingsSaveProfileToolTip": "Uložit profil", + "MenuBarFileToolsTakeScreenshot": "Pořídit snímek obrazovky", + "MenuBarFileToolsHideUi": "Skrýt UI", + "GameListContextMenuRunApplication": "Spustit aplikaci", + "GameListContextMenuToggleFavorite": "Přidat do/Odebrat z oblíbených", + "GameListContextMenuToggleFavoriteToolTip": "Přidat/Odebrat hře oblíbený status", + "SettingsTabGeneralTheme": "Motiv:", + "SettingsTabGeneralThemeAuto": "Auto", + "SettingsTabGeneralThemeDark": "Tmavý", + "SettingsTabGeneralThemeLight": "Světlý", + "ControllerSettingsConfigureGeneral": "Konfigurovat", + "ControllerSettingsRumble": "Vibrace", + "ControllerSettingsRumbleStrongMultiplier": "Násobitel silných vibrací", + "ControllerSettingsRumbleWeakMultiplier": "Násobitel slabých vibrací", + "DialogMessageSaveNotAvailableMessage": "Pro {0} [{1:x16}] neexistují žádná uložená data", + "DialogMessageSaveNotAvailableCreateSaveMessage": "Chcete vytvořit uložená data pro tuto hru?", + "DialogConfirmationTitle": "Ryujinx - Potvrzení", + "DialogUpdaterTitle": "Ryujinx - Aktualizátor", + "DialogErrorTitle": "Ryujinx - Chyba", + "DialogWarningTitle": "Ryujinx - Varování", + "DialogExitTitle": "Ryujinx - Ukončit", + "DialogErrorMessage": "Ryujinx narazil na chybu", + "DialogExitMessage": "Opravdu chcete zavřít Ryujinx?", + "DialogExitSubMessage": "Všechna neuložená data budou ztracena!", + "DialogMessageCreateSaveErrorMessage": "Při vytváření zadaných uložených dat došlo k chybě: {0}", + "DialogMessageFindSaveErrorMessage": "Při hledání zadaných uložených dat došlo k chybě: {0}", + "FolderDialogExtractTitle": "Vyberte složku, do které se bude extrahovat", + "DialogNcaExtractionMessage": "Extrahování {0} sekce z {1}...", + "DialogNcaExtractionTitle": "Ryujinx - Extraktor NCA sekce", + "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extrakce selhala. Hlavní NCA ve vybraném souboru nebyl přítomen.", + "DialogNcaExtractionCheckLogErrorMessage": "Extrakce selhala. Pro další informace si přečtěte soubor logů.", + "DialogNcaExtractionSuccessMessage": "Extrakce byla úspěšně dokončena.", + "DialogUpdaterConvertFailedMessage": "Nepodařilo se převést aktuální verzi Ryujinxu.", + "DialogUpdaterCancelUpdateMessage": "Rušení aktualizace!", + "DialogUpdaterAlreadyOnLatestVersionMessage": "Již používáte nejaktualizovanější verzi Ryujinx!", + "DialogUpdaterFailedToGetVersionMessage": "Došlo k chybě při pokusu získat informace o vydání z GitHubu. To se může stát, pokud GitHub akce sestavují novou verzi. Zkuste to znovu za pár minut.", + "DialogUpdaterConvertFailedGithubMessage": "Nepodařilo se převést přijatou verzi Ryujinxu z Github vydání.", + "DialogUpdaterDownloadingMessage": "Stahování aktualizace...", + "DialogUpdaterExtractionMessage": "Rozbalování aktualizace...", + "DialogUpdaterRenamingMessage": "Přejmenovávání aktualizace...", + "DialogUpdaterAddingFilesMessage": "Přidávání nové aktualizace...", + "DialogUpdaterCompleteMessage": "Aktualizace dokončena!", + "DialogUpdaterRestartMessage": "Chcete nyní restartovat Ryujinx?", + "DialogUpdaterNoInternetMessage": "Nejste připojeni k internetu!", + "DialogUpdaterNoInternetSubMessage": "Ověřte prosím, zda máte funkční připojení k internetu!", + "DialogUpdaterDirtyBuildMessage": "Není možné aktualizovat pracovní sestavení Ryujinxu!", + "DialogUpdaterDirtyBuildSubMessage": "Pokud hledáte podporovanou verzi, stáhněte si Ryujinx na https://ryujinx.org/.", + "DialogRestartRequiredMessage": "Vyžadován restart", + "DialogThemeRestartMessage": "Motiv byl uložen. Restart je nutný pro aplikování motivu.", + "DialogThemeRestartSubMessage": "Chcete restartovat", + "DialogFirmwareInstallEmbeddedMessage": "Chcete nainstalovat firmware obsažený v této hře? (Firmware {0})", + "DialogFirmwareInstallEmbeddedSuccessMessage": "Nebyl nalezen žádný nainstalovaný firmware, ale Ryujinx byl schopen nainstalovat firmware {0} z poskytnuté hry.\nEmulátor se nyní spustí.", + "DialogFirmwareNoFirmwareInstalledMessage": "Není nainstalován žádný firmware", + "DialogFirmwareInstalledMessage": "Firmware {0} byl nainstalován", + "DialogInstallFileTypesSuccessMessage": "Typy souborů byly úspěšně nainstalovány!", + "DialogInstallFileTypesErrorMessage": "Nepodařilo se nainstalovat typy souborů.", + "DialogUninstallFileTypesSuccessMessage": "Typy souborů byly úspěšně odinstalovány!", + "DialogUninstallFileTypesErrorMessage": "Nepodařilo se odinstalovat typy souborů.", + "DialogOpenSettingsWindowLabel": "Otevřít okno nastavení", + "DialogControllerAppletTitle": "Applet ovladače", + "DialogMessageDialogErrorExceptionMessage": "Chyba při zobrazování dialogového okna se zprávou: {0}", + "DialogSoftwareKeyboardErrorExceptionMessage": "Chyba při zobrazování softwarové klávesnice: {0}", + "DialogErrorAppletErrorExceptionMessage": "Chyba při zobrazování ErrorApplet dialogového okna: {0}", + "DialogUserErrorDialogMessage": "{0}: {1}", + "DialogUserErrorDialogInfoMessage": "\nDalší informace o tom, jak opravit tuto chybu, naleznete v našem průvodci nastavením.", + "DialogUserErrorDialogTitle": "Chyba Ryujinxu ({0})", + "DialogAmiiboApiTitle": "Amiibo API", + "DialogAmiiboApiFailFetchMessage": "Nastala chyba při načítání informací z API.", + "DialogAmiiboApiConnectErrorMessage": "Nepodařilo se připojit k serveru Amiibo API. Služba může být mimo provoz nebo možná budete muset ověřit, že jste připojeni k internetu a online.", + "DialogProfileInvalidProfileErrorMessage": "Profil {0} je nekompatibilní s aktuálním konfiguračním systémem vstupu.", + "DialogProfileDefaultProfileOverwriteErrorMessage": "Výchozí profil nelze přepsat", + "DialogProfileDeleteProfileTitle": "Mazání profilu", + "DialogProfileDeleteProfileMessage": "Tato akce je nevratná, opravdu chcete pokračovat?", + "DialogWarning": "Varování", + "DialogPPTCDeletionMessage": "Chystáte se přidat do fronty obnovu PPTC při příštím spuštění:\n\n{0}\n\nOpravdu chcete pokračovat?", + "DialogPPTCDeletionErrorMessage": "Chyba při mazání PPTC mezipaměti na {0}: {1}", + "DialogShaderDeletionMessage": "Chystáte se odstranit mezipaměť shaderu pro:\n\n{0}\n\nOpravdu chcete pokračovat?", + "DialogShaderDeletionErrorMessage": "Chyba při mazání mezipaměti shaderu na {0}: {1}", + "DialogRyujinxErrorMessage": "Ryujinx narazil na chybu", + "DialogInvalidTitleIdErrorMessage": "Chyba UI: Vybraná hra neměla platné ID titulu", + "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Platný firmware systému nebyl nalezen v {0}.", + "DialogFirmwareInstallerFirmwareInstallTitle": "Nainstalovat firmware {0}", + "DialogFirmwareInstallerFirmwareInstallMessage": "Bude nainstalována verze systému {0}.", + "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nTen nahradí aktuální verzi systému {0}.", + "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "Chcete pokračovat?", + "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalování firmwaru...", + "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Verze systému {0} byla úspěšně nainstalována.", + "DialogUserProfileDeletionWarningMessage": "Nebyly by už žádné další profily k otevření, pokud by byl vybraný profil smazán", + "DialogUserProfileDeletionConfirmMessage": "Chcete odstranit vybraný profil", + "DialogUserProfileUnsavedChangesTitle": "Varování - Neuložené změny", + "DialogUserProfileUnsavedChangesMessage": "Provedli jste změny v tomto uživatelském profilu, které nebyly uloženy.", + "DialogUserProfileUnsavedChangesSubMessage": "Chcete zahodit vaše změny?", + "DialogControllerSettingsModifiedConfirmMessage": "Aktuální nastavení ovladače bylo aktualizováno.", + "DialogControllerSettingsModifiedConfirmSubMessage": "Chcete uložit změny?", + "DialogLoadFileErrorMessage": "{0}. Chybný soubor: {1}", + "DialogModAlreadyExistsMessage": "Mód již existuje", + "DialogModInvalidMessage": "Zadaný adresář neobsahuje mód!", + "DialogModDeleteNoParentMessage": "Smazání se nezdařilo: Nepodařilo se najít nadřazený adresář módu \"{0}\"!", + "DialogDlcNoDlcErrorMessage": "Zvolený soubor neobsahuje DLC pro vybraný titul!", + "DialogPerformanceCheckLoggingEnabledMessage": "Máte zapnuté trasovací logy, které jsou navrženy pouze pro vývojáře.", + "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Pro optimální výkon je doporučeno vypnout trasovací logy. Chcete trasovací logy vypnout hned?", + "DialogPerformanceCheckShaderDumpEnabledMessage": "Máte zapnuto dumpování shaderů, které je navrženo pouze pro vývojáře.", + "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Pro optimální výkon je doporučeno vypnout dumpování shaderů. Chcete dumpování shaderů vypnout hned?", + "DialogLoadAppGameAlreadyLoadedMessage": "Hra již byla načtena", + "DialogLoadAppGameAlreadyLoadedSubMessage": "Před spuštěním další hry zastavte emulaci nebo zavřete emulátor.", + "DialogUpdateAddUpdateErrorMessage": "Zvolený soubor neobsahuje aktualizaci pro vybraný titul!", + "DialogSettingsBackendThreadingWarningTitle": "Varování - Threading backendu", + "DialogSettingsBackendThreadingWarningMessage": "Ryujinx musí být restartován po změně této možnosti, aby se mohla plně aplikovat. V závislosti na vaší platformě budete možná muset při používání Ryujinxu ručně vypnout vlastní multithreading vašeho ovladače.", + "DialogModManagerDeletionWarningMessage": "Chystáte se odstranit mód: {0}\n\nOpravdu chcete pokračovat?", + "DialogModManagerDeletionAllWarningMessage": "Chystáte se odstranit všechny módy pro tento titul.\n\nOpravdu chcete pokračovat?", + "SettingsTabGraphicsFeaturesOptions": "Funkce", + "SettingsTabGraphicsBackendMultithreading": "Multithreading grafického backendu:", + "CommonAuto": "Auto", + "CommonOff": "Vypnuto", + "CommonOn": "Zapnuto", + "InputDialogYes": "Ano", + "InputDialogNo": "Ne", + "DialogProfileInvalidProfileNameErrorMessage": "Název souboru obsahuje neplatné znaky. Zkuste to prosím znovu.", + "MenuBarOptionsPauseEmulation": "Pozastavit", + "MenuBarOptionsResumeEmulation": "Pokračovat", + "AboutUrlTooltipMessage": "Kliknutím otevřete stránky Ryujinxu ve vašem výchozím prohlížeči.", + "AboutDisclaimerMessage": "Ryujinx nemá žádnou vazbu na Nintendo™,\nani na žádného z jejich partnerů.", + "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) je použito\nv naší emulaci Amiibo.", + "AboutPatreonUrlTooltipMessage": "Kliknutím otevřete Patreon Ryujinxu ve vašem výchozím prohlížeči.", + "AboutGithubUrlTooltipMessage": "Kliknutím otevřete GitHub Ryujinxu ve vašem výchozím prohlížeči.", + "AboutDiscordUrlTooltipMessage": "Kliknutím otevřete pozvánku na Ryujinx Discord server ve vašem výchozím prohlížeči.", + "AboutTwitterUrlTooltipMessage": "Kliknutím otevřete Twitter Ryujinxu ve vašem výchozím prohlížeči.", + "AboutRyujinxAboutTitle": "O aplikaci:", + "AboutRyujinxAboutContent": "Ryujinx je emulátor Nintenda Switch™.\nPodpořte nás prosím na Patreonu.\nZískejte všechny čerstvé novinky na našem Twitteru nebo Discordu.\nVývojáři se zájmem o přispívání se mohou dozvědět více na našem GitHubu nebo Discordu.", + "AboutRyujinxMaintainersTitle": "Aplikaci vyvíjí:", + "AboutRyujinxMaintainersContentTooltipMessage": "Kliknutím otevřete stránku Přispěvatelů ve vašem výchozím prohlížeči.", + "AboutRyujinxSupprtersTitle": "Na Patreonu nás podporují:", + "AmiiboSeriesLabel": "Amiibo série", + "AmiiboCharacterLabel": "Postava", + "AmiiboScanButtonLabel": "Naskenovat", + "AmiiboOptionsShowAllLabel": "Zobrazit všechna Amiibo", + "AmiiboOptionsUsRandomTagLabel": "Hack: Použít náhodný štítek Uuid", + "DlcManagerTableHeadingEnabledLabel": "Povoleno", + "DlcManagerTableHeadingTitleIdLabel": "ID Titulu", + "DlcManagerTableHeadingContainerPathLabel": "Cesta kontejneru", + "DlcManagerTableHeadingFullPathLabel": "Plná cesta", + "DlcManagerRemoveAllButton": "Odebrat vše", + "DlcManagerEnableAllButton": "Povolit vše", + "DlcManagerDisableAllButton": "Zakázat vše", + "ModManagerDeleteAllButton": "Odstranit vše", + "MenuBarOptionsChangeLanguage": "Změnit jazyk", + "MenuBarShowFileTypes": "Zobrazovat typy souborů", + "CommonSort": "Seřadit", + "CommonShowNames": "Zobrazovat názvy", + "CommonFavorite": "Oblíbené", + "OrderAscending": "Vzestupně", + "OrderDescending": "Sestupně", + "SettingsTabGraphicsFeatures": "Funkce a vylepšení", + "ErrorWindowTitle": "Okno chyby", + "ToggleDiscordTooltip": "Zvolte, zda zobrazovat Ryujinx v sekci \"aktuálně hraje\" na Discord aktivitě", + "AddGameDirBoxTooltip": "Zadejte adresář her pro přidání do seznamu", + "AddGameDirTooltip": "Přidat adresář her do seznamu", + "RemoveGameDirTooltip": "Odebrat vybraný adresář her", + "CustomThemeCheckTooltip": "Použít vlastní motiv Avalonia pro GUI ke změně vzhledu menu emulátoru", + "CustomThemePathTooltip": "Cesta k vlastnímu motivu GUI", + "CustomThemeBrowseTooltip": "Procházet pro vlastní motiv GUI", + "DockModeToggleTooltip": "Emulovaný systém v dokovaném režimu se bude chovat jako dokované Nintendo Switch. To zlepšuje kvalitu grafiky ve většině her. Naopak vypnutím této funkce se emulovaný systém bude chovat jako handheld Nintendo Switch, což sníží kvalitu grafiky.\n\nNakonfigurujte ovládání hráče 1, pokud plánujete použít dokovaný režim; nakonfigurujte handheld ovládání, pokud plánujete použít handheld režim.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "DirectKeyboardTooltip": "Podpora přístupu přímo klávesnicí (HID). Poskytuje hře přístup k vaší klávesnici jako zařízení pro zadávání textu.\n\nFunguje pouze s hrami, které nativně podporují používání klávesnice na hardwaru Switche.\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "DirectMouseTooltip": "Podpora přístupu přímo myší (HID). Poskytuje hře přístup k vaší myši jako zařízení pro ukazování.\n\nFunguje pouze s hrami, které nativně podporují ovládání myší na hardwaru Switche, což jsou pouze ojedinělé případy.\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "RegionTooltip": "Změnit region systému", + "LanguageTooltip": "Změnit jazyk systému", + "TimezoneTooltip": "Změnit časovou zónu systému", + "TimeTooltip": "Změnit čas systému", + "VSyncToggleTooltip": "Vertikální synchronizace emulované konzole. V podstatě omezovač snímků za sekundu pro většinu her; vypnutí může způsobit, že hry poběží rychleji, nebo, že načítání obrazovky bude trvat déle nebo se úplně zasekne.\n\nLze přepnout ve hře pomocí klávesové zkratky dle vaší volby (F1 výchozí). Doporučujeme toto udělat, pokud plánujete synchronizaci vypnout.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "PptcToggleTooltip": "Ukládá přeložené funkce JIT, aby nemusely být překládány pokaždé, když se hra načítá.\n\nSnižuje sekání a výrazně zrychluje časy spuštění po prvním spuštění hry.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "FsIntegrityToggleTooltip": "Při spuštění hry se pokusí vyhledat poškozené soubory a pokud jsou nějaké nalezeny, zobrazí v logu chybu hashe.\n\nNemá žádný vliv na výkon a je určeno k řešení problémů.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "AudioBackendTooltip": "Změní backend používaný k renderování zvuku.\n\nJe preferováno SDL2, zatímco OpenAL a SoundIO jsou používány jako záložní. Fiktivní bude bez zvuku.\n\nNastavte na SDL2, pokud si nejste jistí.", + "MemoryManagerTooltip": "Změnit, jak se paměť hosta mapuje a jak se k ní přistupuje. Výrazně ovlivňuje výkon emulovaného CPU.\n\nNastavte na NEKONTROLOVANÝ HOST, pokud si nejste jistí.", + "MemoryManagerSoftwareTooltip": "Použijte softwarovou tabulku pro překlad adres. Největší přesnost, ale nejpomalejší výkon.", + "MemoryManagerHostTooltip": "Přímo mapovat paměť v adresovém prostoru hostitele. Mnohem rychlejší JIT kompilace a běh.", + "MemoryManagerUnsafeTooltip": "Přímo mapovat paměť, ale před přístupem nemaskovat adresu v adresovém prostoru hosta. Rychlejší, ale za cenu bezpečnosti. Hostovaná aplikace má přístup k paměti kdekoli v Ryujinxu, takže v tomto režimu spuštějte pouze programy, kterým důvěřujete.", + "UseHypervisorTooltip": "Použít Hypervizor namísto JIT. Výrazně zlepšuje výkon, pokud je dostupný, ale může být nestabilní v jeho současném stavu.", + "DRamTooltip": "Využívá alternativního paměťového režimu s 8GiB DRAM k napodobení vývojového modelu Switch.\n\nToto je užitečné pouze pro balíčky textur s vyšším rozlišením nebo pro módy poskytující rozlišení 4k. NEZLEPŠUJE výkon.\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "IgnoreMissingServicesTooltip": "Ignoruje nenaimplementované služby Horizon OS. To může pomoci pro obcházení crashů při spouštění určitých her.\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "GraphicsBackendThreadingTooltip": "Provádí příkazy grafického backendu na druhém vlákně.\n\nZrychluje kompilaci shaderů, snižuje sekání a zlepšuje výkon na GPU ovladačích bez jejich vlastní podpory vícevláknového zpracování. Mírně lepší výkon na ovladačích s podporou vícevláknového zpracování.\n\nNastavte na AUTO, pokud si nejste jistí.", + "GalThreadingTooltip": "Provádí příkazy grafického backendu na druhém vlákně.\n\nZrychluje kompilaci shaderů, snižuje sekání a zlepšuje výkon na GPU ovladačích bez jejich vlastní podpory vícevláknového zpracování. Mírně lepší výkon na ovladačích s podporou vícevláknového zpracování.\n\nNastavte na AUTO, pokud si nejste jistí.", + "ShaderCacheToggleTooltip": "Uloží mezipaměť shaderu na disk, což snižuje sekání při následných spuštěních.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "ResolutionScaleTooltip": "Vynásobí rozlišení, ve kterém se hra vykresluje.\n\nNěkolik her s tímto nastavením nemusí fungovat a mohou vypadat rozpixelovaně, i když je rozlišení zvýšeno; pro takové hry možná budete muset vyhledat módy, které odstraňují vyhlazování hran nebo zvyšují jejich vnitřní vykreslovací rozlišení. Při používáním tohoto druhého typu módu budete pravděpodobně chtít vybrat Nativní.\n\nTuto možnost lze změnit, zatímco hra běží, kliknutím na tlačítko \"Použít\" níže; můžete jednoduše přesunout okno nastavení stranou a experimentovat tak dlouho, dokud nenajdete váš preferovaný vzhled hry.\n\nMějte na paměti, že 4x je až příliš pro prakticky jakýkoli systém.", + "ResolutionScaleEntryTooltip": "Měřítko rozlišení s desetinnými místy, třeba 1.5. Necelé faktory škálování mají větší pravděpodobnost způsobit problémy nebo crash.", + "AnisotropyTooltip": "Úroveň anizotropního filtrování. Nastavte na Auto pro použití hodnoty, kterou si vyžádá hra.", + "AspectRatioTooltip": "Poměr stran aplikován na okno vykreslovače.\n\nZměňte jej pouze, pokud používáte mód měnící poměr stran pro vaši hru, jinak bude grafika roztažena.\n\nPonechte 16:9, pokud si nejste jistí.", + "ShaderDumpPathTooltip": "Cesta pro dumpování grafických shaderů", + "FileLogTooltip": "Uloží logování z konzole do souboru logů na disk. Nemá vliv na výkon.", + "StubLogTooltip": "Vypisuje stub logy v konzoli. Nemá vliv na výkon.", + "InfoLogTooltip": "Vypisuje informační logy v konzoli. Nemá vliv na výkon.", + "WarnLogTooltip": "Vypisuje logy varování v konzoli. Nemá vliv na výkon.", + "ErrorLogTooltip": "Vypisuje logy chyb v konzoli. Nemá vliv na výkon.", + "TraceLogTooltip": "Vypisuje trasovací logy v konzoli. Nemá vliv na výkon.", + "GuestLogTooltip": "Vypisuje logy hosta v konzoli. Nemá vliv na výkon.", + "FileAccessLogTooltip": "Vypisuje logy přístupů k souborům do konzole.", + "FSAccessLogModeTooltip": "Povolí logování přístupů k souborovému systému do konzole. Možné režimy jsou 0-3", + "DeveloperOptionTooltip": "Používejte opatrně", + "OpenGlLogLevel": "Vyžaduje mít povolené odpovídající úrovně logů", + "DebugLogTooltip": "Vypisuje logy pro ladění do konzole.\n\nPoužijte toto pouze v případě, že vám výslovně dá pokyn člen personálu, protože to sníží čitelnost logů a zhorší výkon emulátoru.", + "LoadApplicationFileTooltip": "Otevřít průzkumníka souborů pro výběr souboru kompatibilního se Switchem k načtení", + "LoadApplicationFolderTooltip": "Otevřít průzkumníka souborů pro výběr rozbalené aplikace, kompatibilní se Switchem, k načtení", + "OpenRyujinxFolderTooltip": "Otevřít složku souborového systému Ryujinxu", + "OpenRyujinxLogsTooltip": "Otevře složku, do které se zapisují logy", + "ExitTooltip": "Ukončit Ryujinx", + "OpenSettingsTooltip": "Otevřít okno nastavení", + "OpenProfileManagerTooltip": "Otevřít okno správce uživatelských profilů", + "StopEmulationTooltip": "Zastavit emulaci aktuální hry a vrátit se na výběr her", + "CheckUpdatesTooltip": "Zkontrolovat aktualizace Ryujinxu", + "OpenAboutTooltip": "Otevřít okno \"O aplikaci\"", + "GridSize": "Velikost mřížky", + "GridSizeTooltip": "Změnit velikost položek mřížky", + "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brazilská portugalština", + "AboutRyujinxContributorsButtonHeader": "Zobrazit všechny přispěvatele", + "SettingsTabSystemAudioVolume": "Hlasitost: ", + "AudioVolumeTooltip": "Změnit hlasitost zvuku", + "SettingsTabSystemEnableInternetAccess": "Přistup hosta k internetu/LAN režim", + "EnableInternetAccessTooltip": "Umožňuje emulované aplikaci připojit se k internetu.\n\nHry s LAN módem se mohou vzájemně k sobě připojit, pokud je tato možnost povolena a systémy jsou připojeny ke stejnému přístupovému bodu. To zahrnuje také reálné konzole.\n\nNEUMOŽŇUJE připojení se k Nintendo serverům. Může způsobit crash v určitých hrách, které se snaží připojit k Internetu.\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "GameListContextMenuManageCheatToolTip": "Spravovat cheaty", + "GameListContextMenuManageCheat": "Spravovat cheaty", + "GameListContextMenuManageModToolTip": "Spravovat módy", + "GameListContextMenuManageMod": "Spravovat módy", + "ControllerSettingsStickRange": "Rozsah:", + "DialogStopEmulationTitle": "Ryujinx - Ukončit emulaci", + "DialogStopEmulationMessage": "Opravdu chcete ukončit emulaci?", + "SettingsTabCpu": "CPU", + "SettingsTabAudio": "Zvuk", + "SettingsTabNetwork": "Síť", + "SettingsTabNetworkConnection": "Síťové připojení", + "SettingsTabCpuCache": "Cache CPU", + "SettingsTabCpuMemory": "Režim CPU", + "DialogUpdaterFlatpakNotSupportedMessage": "Aktualizujte prosím Ryujinx přes FlatHub.", + "UpdaterDisabledWarningTitle": "Aktualizátor zakázán!", + "ControllerSettingsRotate90": "Otočit o 90° po směru hodinových ručiček", + "IconSize": "Velikost ikon", + "IconSizeTooltip": "Změnit velikost herních ikon", + "MenuBarOptionsShowConsole": "Zobrazit konzoli", + "ShaderCachePurgeError": "Chyba při mazání mezipaměti shaderu na {0}: {1}", + "UserErrorNoKeys": "Klíče nebyly nalezeny", + "UserErrorNoFirmware": "Firmware nebyl nalezen", + "UserErrorFirmwareParsingFailed": "Chyba při zpracování firmwaru", + "UserErrorApplicationNotFound": "Aplikace nebyla nalezena", + "UserErrorUnknown": "Neznámá chyba", + "UserErrorUndefined": "Nedefinovaná chyba", + "UserErrorNoKeysDescription": "Ryujinx nedokázal najít váš 'prod.keys' soubor", + "UserErrorNoFirmwareDescription": "Ryujinx nedokázal najít žádné nainstalované firmwary", + "UserErrorFirmwareParsingFailedDescription": "Ryujinx nebyl schopen zpracovat poskytnutý firmware. To je obvykle způsobeno zastaralými klíči.", + "UserErrorApplicationNotFoundDescription": "Ryujinx nemohl najít platnou aplikaci v dané cestě.", + "UserErrorUnknownDescription": "Došlo k neznámé chybě!", + "UserErrorUndefinedDescription": "Došlo k nedefinované chybě! Toto by se nemělo stát, kontaktujte prosím vývojáře!", + "OpenSetupGuideMessage": "Otevřít průvodce nastavením", + "NoUpdate": "Žádná aktualizace", + "TitleUpdateVersionLabel": "Verze {0}", + "TitleBundledUpdateVersionLabel": "Obsažena: Verze {0}", + "TitleBundledDlcLabel": "Obsaženo:", + "RyujinxInfo": "Ryujinx - Info", + "RyujinxConfirm": "Ryujinx - Potvrzení", + "FileDialogAllTypes": "Všechny typy", + "Never": "Nikdy", + "SwkbdMinCharacters": "Musí být alespoň {0} znaků dlouhé", + "SwkbdMinRangeCharacters": "Musí být {0}-{1} znaků dlouhé", + "SoftwareKeyboard": "Softwarová klávesnice", + "SoftwareKeyboardModeNumeric": "Musí obsahovat pouze 0-9 nebo '.'", + "SoftwareKeyboardModeAlphabet": "Nesmí obsahovat žádné CJK znaky", + "SoftwareKeyboardModeASCII": "Musí obsahovat pouze ASCII text", + "ControllerAppletControllers": "Podporované ovladače:", + "ControllerAppletPlayers": "Hráči:", + "ControllerAppletDescription": "Vaše aktuální konfigurace je neplatná. Otevřete nastavení a znovu nastavte své vstupy ovladače.", + "ControllerAppletDocked": "Nastaven dokovaný režim. Handheld ovládání by mělo být vypnuto.", + "UpdaterRenaming": "Přejmenovávání starých souborů...", + "UpdaterRenameFailed": "Aktualizátor nemohl přejmenovat soubor: {0}", + "UpdaterAddingFiles": "Přidávání nových souborů...", + "UpdaterExtracting": "Rozbalování aktualizace...", + "UpdaterDownloading": "Stahování aktualizace...", + "Game": "Hra", + "Docked": "Dokovaný", + "Handheld": "Handheld", + "ConnectionError": "Chyba připojení.", + "AboutPageDeveloperListMore": "{0} a další...", + "ApiError": "Chyba API.", + "LoadingHeading": "Načítání {0}", + "CompilingPPTC": "Kompilace PTC", + "CompilingShaders": "Kompilace shaderů", + "AllKeyboards": "Všechny klávesnice", + "OpenFileDialogTitle": "Vyberte podporovaný soubor k otevření", + "OpenFolderDialogTitle": "Vyberte složku s rozbalenou hrou", + "AllSupportedFormats": "Všechny podporované formáty", + "RyujinxUpdater": "Ryujinx aktualizátor", + "SettingsTabHotkeys": "Klávesové zkratky", + "SettingsTabHotkeysHotkeys": "Klávesové zkratky", + "SettingsTabHotkeysToggleVsyncHotkey": "Zapnout/Vypnout VSync:", + "SettingsTabHotkeysScreenshotHotkey": "Pořídit snímek obrazovky:", + "SettingsTabHotkeysShowUiHotkey": "Zobrazit UI:", + "SettingsTabHotkeysPauseHotkey": "Pozastavit:", + "SettingsTabHotkeysToggleMuteHotkey": "Ztlumit:", + "ControllerMotionTitle": "Nastavení ovládání pohybu", + "ControllerRumbleTitle": "Nastavení vibrací", + "SettingsSelectThemeFileDialogTitle": "Vybrat soubor motivu", + "SettingsXamlThemeFile": "Xaml soubor motivu", + "AvatarWindowTitle": "Správa účtů - Avatar", + "Amiibo": "Amiibo", + "Unknown": "Neznámý", + "Usage": "Využití", + "Writable": "Zapisovatelný", + "SelectDlcDialogTitle": "Vyberte soubory DLC", + "SelectUpdateDialogTitle": "Vyberte soubory aktualizací", + "SelectModDialogTitle": "Vyberte adresář módů", + "UserProfileWindowTitle": "Správce uživatelských profilů", + "CheatWindowTitle": "Správce cheatů", + "DlcWindowTitle": "Spravovat stahovatelný obsah pro {0} ({1})", + "ModWindowTitle": "Spravovat módy pro {0} ({1})", + "UpdateWindowTitle": "Správce aktualizací titulu", + "CheatWindowHeading": "Dostupné cheaty pro {0} [{1}]", + "BuildId": "BuildId:", + "DlcWindowHeading": "{0} Stahovatelný obsah(y/ů)", + "ModWindowHeading": "{0} Mód(y/ů)", + "UserProfilesEditProfile": "Upravit vybrané", + "Cancel": "Zrušit", + "Save": "Uložit", + "Discard": "Zahodit", + "Paused": "Pozastaveno", + "UserProfilesSetProfileImage": "Nastavit profilový obrázek", + "UserProfileEmptyNameError": "Jméno je nutné vyplnit", + "UserProfileNoImageError": "Profilový obrázek musí být nastaven", + "GameUpdateWindowHeading": "Spravovat aktualizace pro {0} ({1})", + "SettingsTabHotkeysResScaleUpHotkey": "Zvýšit rozlišení:", + "SettingsTabHotkeysResScaleDownHotkey": "Snížit rozlišení:", + "UserProfilesName": "Jméno:", + "UserProfilesUserId": "ID uživatele:", + "SettingsTabGraphicsBackend": "Grafický backend", + "SettingsTabGraphicsBackendTooltip": "Vyberte grafický backend, který bude použit v emulátoru.\n\nVulkan je celkově lepší pro všechny moderní grafické karty, pokud jsou jejich ovladače aktuální. Vulkan také obsahuje rychlejší kompilaci shaderů (méně sekání) pro GPU od všech prodejců.\n\nOpenGL může dosáhnout lepších výsledků na starých Nvidia GPUs, na starých AMD GPUs na Linuxu, nebo na GPUs s menší VRAM, avšak sekání při kompilaci shaderů budou větší.\n\nNastavte na Vulkan, pokud si nejste jistí. Nastavte na OpenGL, pokud vaše GPU nepodporuje Vulkan ani s nejnovějšími grafickými ovladači.", + "SettingsEnableTextureRecompression": "Povolit rekompresi textur", + "SettingsEnableTextureRecompressionTooltip": "Kompresuje ASTC textury s cílem snížit využití VRAM.\n\nHry používající tento formát textur zahrnují Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder a The Legend of Zelda: Tears of the Kingdom.\n\nGrafické karty s 4GiB VRAM nebo méně nejspíše někdy při běhu těchto her crashnou.\n\nPovolte pouze v případě, že vám ve výše uvedených hrách dochází VRAM. Ponechte VYPNUTO, pokud si nejste jistí.", + "SettingsTabGraphicsPreferredGpu": "Preferované GPU", + "SettingsTabGraphicsPreferredGpuTooltip": "Vyberte grafickou kartu, která bude použita s grafickým backendem Vulkan.\n\nNemá vliv na GPU, které bude používat OpenGL.\n\nNastavte na GPU označené jako \"dGPU\", pokud si nejste jistí. Pokud tam není, hodnotu neměňte.", + "SettingsAppRequiredRestartMessage": "Vyžadován restart Ryujinxu", + "SettingsGpuBackendRestartMessage": "Nastavení grafického backendu nebo GPU byla změněna. Je vyžadován restart na aplikování", + "SettingsGpuBackendRestartSubMessage": "Chcete nyní restartovat?", + "RyujinxUpdaterMessage": "Chcete aktualizovat Ryujinx na nejnovější verzi?", + "SettingsTabHotkeysVolumeUpHotkey": "Zvýšit hlasitost:", + "SettingsTabHotkeysVolumeDownHotkey": "Snížit hlasitost:", + "SettingsEnableMacroHLE": "Povolit HLE makra", + "SettingsEnableMacroHLETooltip": "Vysokoúrovňová emulace GPU makro kódu.\n\nZlepšuje výkon, ale může způsobovat grafické glitche v některých hrách.\n\nPonechte ZAPNUTO, pokud si nejste jistí.", + "SettingsEnableColorSpacePassthrough": "Obejít specifikování barevného prostoru", + "SettingsEnableColorSpacePassthroughTooltip": "Přikáže Vulkan backendu posílat informace o barvě bez specifikování barevného prostoru. Pro uživatele, kteří mají displeje se širokým gamutem, je takto možné docílit živějších barev, za cenu jejich korektnosti.", + "VolumeShort": "Hlasitost", + "UserProfilesManageSaves": "Spravovat uložená data", + "DeleteUserSave": "Chcete odstranit uložená data uživatele pro tuto hru?", + "IrreversibleActionNote": "Tato akce je nevratná.", + "SaveManagerHeading": "Spravovat uložená data pro {0} ({1})", + "SaveManagerTitle": "Správce uložených dat", + "Name": "Jméno", + "Size": "Velikost", + "Search": "Hledat", + "UserProfilesRecoverLostAccounts": "Obnovit ztracené účty", + "Recover": "Obnovit", + "UserProfilesRecoverHeading": "Byla nalezena uložená data pro následující účty", + "UserProfilesRecoverEmptyList": "Žádné profily k obnovení", + "GraphicsAATooltip": "Aplikuje vyhlazování hran na vykreslování her.\n\nFXAA rozostří většinu obrazu, zatímco SMAA se pokusí najít rozostřené hrany a vyhladit je.\n\nNedoporučuje se používat ve spojení se filtrem škálování FSR.\n\nTuto možnost lze změnit, zatímco hra běží, kliknutím na tlačítko \"Použít\" níže; můžete jednoduše přesunout okno nastavení stranou a experimentovat tak dlouho, dokud nenajdete váš preferovaný vzhled hry.\n\nPonechte ŽÁDNÉ, pokud si nejste jistí.", + "GraphicsAALabel": "Vyhlazování hran:", + "GraphicsScalingFilterLabel": "Filtr škálování:", + "GraphicsScalingFilterTooltip": "Vyberte filtr škálování, který bude použit při změně velikosti rozlišení.\n\nBilineární funguje dobře pro 3D hry a je bezpečnou výchozí volbou.\n\nNejbližší je doporučen pro hry s pixelovou grafikou.\n\nFSR 1.0 je pouze ostřící filtr, nedoporučuje se používat s FXAA nebo SMAA.\n\nŠkálování oblasti je doporučeno pro škálování rozlišeních, která jsou větší než výstupní okno. Lze jej použít k dosažení supersamplingového efektu vyhlazených hran při zmenšení o více než 2x.\n\nTuto možnost lze změnit, zatímco hra běží, kliknutím na tlačítko \"Použít\" níže; můžete jednoduše přesunout okno nastavení stranou a experimentovat tak dlouho, dokud nenajdete váš preferovaný vzhled hry.\n\nPonechte BILINEÁRNÍ, pokud si nejste jistí.", + "GraphicsScalingFilterBilinear": "Bilineární", + "GraphicsScalingFilterNearest": "Nejbližší", + "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Oblast", + "GraphicsScalingFilterLevelLabel": "Úroveň", + "GraphicsScalingFilterLevelTooltip": "Nastavit úroveň ostrosti pro FSR 1.0. Vyšší je ostřejší.", + "SmaaLow": "SMAA Nízké", + "SmaaMedium": "SMAA Střední", + "SmaaHigh": "SMAA Vysoké", + "SmaaUltra": "SMAA Ultra", + "UserEditorTitle": "Upravit uživatele", + "UserEditorTitleCreate": "Vytvořit uživatele", + "SettingsTabNetworkInterface": "Síťové rozhraní:", + "NetworkInterfaceTooltip": "Síťové rozhraní používané pro funkce LAN/LDN.\n\nVe spojení s VPN nebo XLink Kai a hrou podporující LAN lze použít pro imitaci připojení po stejné síti přes internet.\n\nPonechte VÝCHOZÍ, pokud si nejste jistí.", + "NetworkInterfaceDefault": "Výchozí", + "PackagingShaders": "Balení shaderů", + "AboutChangelogButton": "Zobrazit seznam změn na GitHubu", + "AboutChangelogButtonTooltipMessage": "Kliknutím otevřete seznam změn pro tuto verzi ve vašem výchozím prohlížeči.", + "SettingsTabNetworkMultiplayer": "Hra více hráčů", + "MultiplayerMode": "Režim:", + "MultiplayerModeTooltip": "Změnit LDN režim hry pro více hráčů.\n\nLdnMitm bude upravovat funkcionalitu místního bezdrátového připojení/místního hraní ve hrách, aby fungovala, jako by to byla LAN, a umožňovala lokální připojení po stejné síti s dalšími Ryujinx instancemi a hacknutými Nintendo Switch konzolemi, které mají nainstalovaný modul ldn_mitm.\n\nHra pro více hráčů vyžaduje, aby byli všichni hráči na stejné verzi hry (např. Super Smash Bros. Ultimate v13.0.1 se nemůže připojit k v13.0.0).\n\nPonechte VYPNUTO, pokud si nejste jistí.", + "MultiplayerModeDisabled": "Vypnuto", + "MultiplayerModeLdnMitm": "ldn_mitm" +} diff --git a/src/Ryujinx/Assets/Locales/de_DE.json b/src/Ryujinx/Assets/Locales/de_DE.json index 401293198..453b4c6b8 100644 --- a/src/Ryujinx/Assets/Locales/de_DE.json +++ b/src/Ryujinx/Assets/Locales/de_DE.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Hypervisor verwenden", "MenuBarFile": "_Datei", "MenuBarFileOpenFromFile": "Datei _öffnen", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "_Entpacktes Spiel öffnen", "MenuBarFileOpenEmuFolder": "Ryujinx-Ordner öffnen", "MenuBarFileOpenLogsFolder": "Logs-Ordner öffnen", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Kann Fehler verursachen)", - "SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GiB", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignoriere fehlende Dienste", "SettingsTabGraphics": "Grafik", "SettingsTabGraphicsAPI": "Grafik-API", @@ -272,14 +273,14 @@ "ControllerSettingsSave": "Speichern", "ControllerSettingsClose": "Schließen", "KeyUnknown": "Unbekannt", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", + "KeyShiftLeft": "Shift Links", + "KeyShiftRight": "Shift Rechts", + "KeyControlLeft": "Strg Links", + "KeyMacControlLeft": "^ Links", + "KeyControlRight": "Strg Rechts", + "KeyMacControlRight": "^ Rechts", + "KeyAltLeft": "", + "KeyMacAltLeft": "⌥ Links", "KeyAltRight": "Alt Right", "KeyMacAltRight": "⌥ Right", "KeyWinLeft": "⊞ Left", @@ -346,7 +347,7 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", + "KeyUnbound": "Nicht zugewiesen", "GamepadLeftStick": "L Stick Button", "GamepadRightStick": "R Stick Button", "GamepadLeftShoulder": "Left Shoulder", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Als Favoriten hinzufügen/entfernen", "GameListContextMenuToggleFavoriteToolTip": "Aktiviert den Favoriten-Status des Spiels", "SettingsTabGeneralTheme": "Design:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "Dunkel", "SettingsTabGeneralThemeLight": "Hell", "ControllerSettingsConfigureGeneral": "Konfigurieren", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "Direkte Zuordnung von Speicher im Host-Adressraum. Viel schnellere JIT-Kompilierung und Ausführung.", "MemoryManagerUnsafeTooltip": "Direkte Zuordnung des Speichers, aber keine Maskierung der Adresse innerhalb des Gastadressraums vor dem Zugriff. Schneller, aber auf Kosten der Sicherheit. Die Gastanwendung kann von überall in Ryujinx auf den Speicher zugreifen, daher sollte in diesem Modus nur Programme ausgeführt werden denen vertraut wird.", "UseHypervisorTooltip": "Verwende Hypervisor anstelle von JIT. Verbessert die Leistung stark, falls vorhanden, kann jedoch in seinem aktuellen Zustand instabil sein.", - "DRamTooltip": "Erhöht den Arbeitsspeicher des emulierten Systems von 4 GiB auf 6 GiB.\n\nDies ist nur für Texturenpakete mit höherer Auflösung oder Mods mit 4K-Auflösung nützlich. Diese Option verbessert NICHT die Leistung.\n\nIm Zweifelsfall AUS lassen.", + "DRamTooltip": "Verwendet einen alternativen Arbeitsspeichermodus mit 8GiB DRAM um ein Switch-Entwicklungsmodell nachzuahmen.\n\nDies ist nur für hochauflösende Texturpakete oder Mods mit 4k Auflösung nützlich. Kann die Leistung NICHT verbessern.\n\n AUS lassen, falls Unsicherheit besteht.", "IgnoreMissingServicesTooltip": "Durch diese Option werden nicht implementierte Dienste der Switch-Firmware ignoriert. Dies kann dabei helfen, Abstürze beim Starten bestimmter Spiele zu umgehen.\n\nIm Zweifelsfall AUS lassen.", "GraphicsBackendThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf AUTO stellen.", "GalThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies Beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf auf AUTO stellen.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Öffne den 'Setup Guide'", "NoUpdate": "Kein Update", "TitleUpdateVersionLabel": "Version {0} - {1}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Bestätigung", "FileDialogAllTypes": "Alle Typen", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nLassen Sie die Option auf NONE, wenn Sie unsicher sind.", "GraphicsAALabel": "Antialiasing:", "GraphicsScalingFilterLabel": "Skalierungsfilter:", - "GraphicsScalingFilterTooltip": "Wählen Sie den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nBleiben Sie auf BILINEAR, wenn Sie unsicher sind.", + "GraphicsScalingFilterTooltip": "Wählen Sie den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nBeim Runterskalieren von Auflösungen, die größer als das Ausgabefenster sind, wird eine Flächenskalierung empfohlen. Es kann verwendet werden, um einen supergesampelten Anti-Aliasing-Effekt beim Runterskalieren um mehr als 2x zu erzielen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nBleiben sie auf BILINEAR, wenn sie unsicher sind.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nächstes", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Fläche", "GraphicsScalingFilterLevelLabel": "Stufe", "GraphicsScalingFilterLevelTooltip": "FSR 1.0 Schärfelevel festlegen. Höher ist schärfer.", "SmaaLow": "SMAA Niedrig", diff --git a/src/Ryujinx/Assets/Locales/el_GR.json b/src/Ryujinx/Assets/Locales/el_GR.json index ccdf6e0e4..90e7b2693 100644 --- a/src/Ryujinx/Assets/Locales/el_GR.json +++ b/src/Ryujinx/Assets/Locales/el_GR.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Χρήση Hypervisor", "MenuBarFile": "_Αρχείο", "MenuBarFileOpenFromFile": "_Φόρτωση Αρχείου Εφαρμογής", + "MenuBarFileOpenFromFileError": "Δεν εντοπίστηκαν εφαρμογές στο επιλεγμένο αρχείο.", "MenuBarFileOpenUnpacked": "Φόρτωση Απακετάριστου _Παιχνιδιού", "MenuBarFileOpenEmuFolder": "Άνοιγμα Φακέλου Ryujinx", "MenuBarFileOpenLogsFolder": "Άνοιγμα Φακέλου Καταγραφής", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Διαχείριση τύπων αρχείων", "MenuBarToolsInstallFileTypes": "Εγκαταστήσετε τύπους αρχείων.", "MenuBarToolsUninstallFileTypes": "Απεγκαταστήσετε τύπους αρχείων", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Εμφάνιση", + "MenuBarViewWindow": "Μέγεθος Παραθύρου", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Βοήθεια", @@ -76,11 +77,11 @@ "GameListContextMenuExtractDataLogoToolTip": "Εξαγωγή της ενότητας Logo από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)", "GameListContextMenuCreateShortcut": "Δημιουργία Συντόμευσης Εφαρμογής", "GameListContextMenuCreateShortcutToolTip": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή", - "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", - "GameListContextMenuOpenModsDirectory": "Open Mods Directory", - "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", - "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", + "GameListContextMenuCreateShortcutToolTipMacOS": "Δημιουργία συντόμευσης στο φάκελο Εφαρμογές του macOS που εκκινεί την επιλεγμένη εφαρμογή", + "GameListContextMenuOpenModsDirectory": "Άνοιγμα Φακέλου των Mods", + "GameListContextMenuOpenModsDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει τα Mods της εφαρμογής", + "GameListContextMenuOpenSdModsDirectory": "Άνοιγμα της Τοποθεσίας των Atmosphere Mods", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Ανοίγει την εναλλακτική τοποθεσία Atmosphere στην κάρτα SD η οποία περιέχει τα Mods της εφαρμογής. Χρήσιμο για mods τα οποία είναι φτιαγμένη για την πραγματική κονσόλα.", "StatusBarGamesLoaded": "{0}/{1} Φορτωμένα Παιχνίδια", "StatusBarSystemVersion": "Έκδοση Συστήματος: {0}", "LinuxVmMaxMapCountDialogTitle": "Εντοπίστηκε χαμηλό όριο για αντιστοιχίσεις μνήμης", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Ενεργοποίηση Εμπλουτισμένης Παρουσίας Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Έλεγχος για Ενημερώσεις στην Εκκίνηση", "SettingsTabGeneralShowConfirmExitDialog": "Εμφάνιση διαλόγου \"Επιβεβαίωση Εξόδου\".", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Απομνημόνευση Μεγέθους/Θέσης Παραθύρου", "SettingsTabGeneralHideCursor": "Απόκρυψη Κέρσορα:", "SettingsTabGeneralHideCursorNever": "Ποτέ", "SettingsTabGeneralHideCursorOnIdle": "Απόκρυψη Δρομέα στην Αδράνεια", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Μικροδιορθώσεις", "SettingsTabSystemHacksNote": " (Μπορεί να προκαλέσουν αστάθεια)", - "SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GiB", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Αγνόηση υπηρεσιών που λείπουν", "SettingsTabGraphics": "Γραφικά", "SettingsTabGraphicsAPI": "API Γραφικά", @@ -160,7 +161,7 @@ "SettingsTabGraphicsResolutionScaleNative": "Εγγενής (720p/1080p)", "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)", + "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Δε συνιστάται)", "SettingsTabGraphicsAspectRatio": "Αναλογία Απεικόνισης:", "SettingsTabGraphicsAspectRatio4x3": "4:3", "SettingsTabGraphicsAspectRatio16x9": "16:9", @@ -271,26 +272,26 @@ "ControllerSettingsMotionGyroDeadzone": "Νεκρή Ζώνη Γυροσκοπίου:", "ControllerSettingsSave": "Αποθήκευση", "ControllerSettingsClose": "Κλείσιμο", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyUnknown": "Άγνωστο", + "KeyShiftLeft": "Αριστερό Shift", + "KeyShiftRight": "Δεξί Shift", + "KeyControlLeft": "Αριστερό Ctrl", + "KeyMacControlLeft": "Αριστερό ^", + "KeyControlRight": "Δεξί Ctrl", + "KeyMacControlRight": "Δεξί ^", + "KeyAltLeft": "Αριστερό Alt", + "KeyMacAltLeft": "Αριστερό ⌥", + "KeyAltRight": "Δεξί Alt", + "KeyMacAltRight": "Δεξί ⌥", + "KeyWinLeft": "Αριστερό ⊞", + "KeyMacWinLeft": "Αριστερό ⌘", + "KeyWinRight": "Δεξί ⊞", + "KeyMacWinRight": "Δεξί ⌘", + "KeyMenu": "Μενού", + "KeyUp": "Πάνω", + "KeyDown": "Κάτω", + "KeyLeft": "Αριστερά", + "KeyRight": "Δεξιά", "KeyEnter": "Enter", "KeyEscape": "Escape", "KeySpace": "Space", @@ -318,11 +319,11 @@ "KeyKeypad7": "Keypad 7", "KeyKeypad8": "Keypad 8", "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", + "KeyKeypadDivide": "Πλήκτρο Διαίρεσης Keypad", + "KeyKeypadMultiply": "Πλήκτρο Πολλαπλασιασμού Keypad", + "KeyKeypadSubtract": "Πλήκτρο Αφαίρεσης Keypad", + "KeyKeypadAdd": "Πλήκτρο Πρόσθεσης Keypad", + "KeyKeypadDecimal": "Πλήκτρο Δεκαδικού Keypad", "KeyKeypadEnter": "Keypad Enter", "KeyNumber0": "0", "KeyNumber1": "1", @@ -346,17 +347,17 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", + "KeyUnbound": "Αόριστο", "GamepadLeftStick": "L Stick Button", "GamepadRightStick": "R Stick Button", "GamepadLeftShoulder": "Left Shoulder", "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "GamepadLeftTrigger": "Αριστερή Σκανδάλη", + "GamepadRightTrigger": "Δεξιά Σκανδάλη", + "GamepadDpadUp": "Πάνω", + "GamepadDpadDown": "Κάτω", + "GamepadDpadLeft": "Αριστερά", + "GamepadDpadRight": "Δεξιά", "GamepadMinus": "-", "GamepadPlus": "+", "GamepadGuide": "Guide", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Εναλλαγή Αγαπημένου", "GameListContextMenuToggleFavoriteToolTip": "Εναλλαγή της Κατάστασης Αγαπημένο του Παιχνιδιού", "SettingsTabGeneralTheme": "Theme:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "Dark", "SettingsTabGeneralThemeLight": "Light", "ControllerSettingsConfigureGeneral": "Παραμέτρων", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "Απευθείας αντιστοίχιση της μνήμης στον χώρο διευθύνσεων υπολογιστή υποδοχής. Πολύ πιο γρήγορη μεταγλώττιση και εκτέλεση JIT.", "MemoryManagerUnsafeTooltip": "Απευθείας χαρτογράφηση της μνήμης, αλλά μην καλύπτετε τη διεύθυνση εντός του χώρου διευθύνσεων επισκέπτη πριν από την πρόσβαση. Πιο γρήγορα, αλλά με κόστος ασφάλειας. Η εφαρμογή μπορεί να έχει πρόσβαση στη μνήμη από οπουδήποτε στο Ryujinx, επομένως εκτελείτε μόνο προγράμματα που εμπιστεύεστε με αυτήν τη λειτουργία.", "UseHypervisorTooltip": "Χρησιμοποιήστε Hypervisor αντί για JIT. Βελτιώνει σημαντικά την απόδοση όταν διατίθεται, αλλά μπορεί να είναι ασταθής στην τρέχουσα κατάστασή του.", - "DRamTooltip": "Επεκτείνει την ποσότητα της μνήμης στο εξομοιούμενο σύστημα από 4 GiB σε 6 GiB", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ενεργοποίηση ή απενεργοποίηση της αγνοώησης για υπηρεσίες που λείπουν", "GraphicsBackendThreadingTooltip": "Ενεργοποίηση Πολυνηματικής Επεξεργασίας Γραφικών", "GalThreadingTooltip": "Εκτελεί εντολές γραφικών σε ένα δεύτερο νήμα. Επιτρέπει την πολυνηματική μεταγλώττιση Shader σε χρόνο εκτέλεσης, μειώνει το τρεμόπαιγμα και βελτιώνει την απόδοση των προγραμμάτων οδήγησης χωρίς τη δική τους υποστήριξη πολλαπλών νημάτων. Ποικίλες κορυφαίες επιδόσεις σε προγράμματα οδήγησης με multithreading. Μπορεί να χρειαστεί επανεκκίνηση του Ryujinx για να απενεργοποιήσετε σωστά την ενσωματωμένη λειτουργία πολλαπλών νημάτων του προγράμματος οδήγησης ή ίσως χρειαστεί να το κάνετε χειροκίνητα για να έχετε την καλύτερη απόδοση.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Ανοίξτε τον Οδηγό Εγκατάστασης.", "NoUpdate": "Καμία Eνημέρωση", "TitleUpdateVersionLabel": "Version {0} - {1}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Πληροφορίες", "RyujinxConfirm": "Ryujinx - Επιβεβαίωση", "FileDialogAllTypes": "Όλοι οι τύποι", @@ -754,12 +758,13 @@ "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAALabel": "Anti-Aliasing", "GraphicsScalingFilterLabel": "Φίλτρο Κλιμάκωσης:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", + "GraphicsScalingFilterNearest": "Πλησιέστερο", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Επίπεδο", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", + "GraphicsScalingFilterLevelTooltip": "Ορισμός επιπέδου οξύνσης της εικόνας του FSR 1.0. Το υψηλότερο είναι και πιο οξύ.", "SmaaLow": "Χαμηλό SMAA", "SmaaMedium": " Μεσαίο SMAA", "SmaaHigh": "Υψηλό SMAA", @@ -775,6 +780,6 @@ "SettingsTabNetworkMultiplayer": "Πολλαπλοί παίκτες", "MultiplayerMode": "Λειτουργία:", "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", + "MultiplayerModeDisabled": "Απενεργοποιημένο", "MultiplayerModeLdnMitm": "ldn_mitm" } diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index b3cab7f5f..8df0f96a1 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -10,7 +10,6 @@ "SettingsTabSystemUseHypervisor": "Use Hypervisor", "MenuBarFile": "_File", "MenuBarFileOpenFromFile": "_Load Application From File", - "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "Load _Unpacked Game", "MenuBarFileOpenEmuFolder": "Open Ryujinx Folder", "MenuBarFileOpenLogsFolder": "Open Logs Folder", @@ -145,7 +144,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": "May cause instability", - "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", + "SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)", "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", "SettingsTabGraphics": "Graphics", "SettingsTabGraphicsAPI": "Graphics API", @@ -575,7 +574,7 @@ "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", "UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", - "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", + "DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", @@ -650,8 +649,6 @@ "OpenSetupGuideMessage": "Open the Setup Guide", "NoUpdate": "No Update", "TitleUpdateVersionLabel": "Version {0}", - "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", - "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Confirmation", "FileDialogAllTypes": "All types", @@ -758,11 +755,10 @@ "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAALabel": "Anti-Aliasing:", "GraphicsScalingFilterLabel": "Scaling Filter:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Level", "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", "SmaaLow": "SMAA Low", diff --git a/src/Ryujinx/Assets/Locales/es_ES.json b/src/Ryujinx/Assets/Locales/es_ES.json index e58fa5dcf..3301bd467 100644 --- a/src/Ryujinx/Assets/Locales/es_ES.json +++ b/src/Ryujinx/Assets/Locales/es_ES.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Usar hipervisor", "MenuBarFile": "_Archivo", "MenuBarFileOpenFromFile": "_Cargar aplicación desde un archivo", + "MenuBarFileOpenFromFileError": "No se han encontrado aplicaciones en el archivo seleccionado.", "MenuBarFileOpenUnpacked": "Cargar juego _desempaquetado", "MenuBarFileOpenEmuFolder": "Abrir carpeta de Ryujinx", "MenuBarFileOpenLogsFolder": "Abrir carpeta de registros", @@ -31,8 +32,8 @@ "MenuBarToolsInstallFileTypes": "Instalar tipos de archivo", "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo", "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", + "MenuBarViewWindow": "Tamaño de ventana", + "MenuBarViewWindow720": "720P", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Ayuda", "MenuBarHelpCheckForUpdates": "Buscar actualizaciones", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar estado en Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Buscar actualizaciones al iniciar", "SettingsTabGeneralShowConfirmExitDialog": "Mostrar diálogo de confirmación al cerrar", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Recordar tamaño y posición de la ventana", "SettingsTabGeneralHideCursor": "Esconder el cursor:", "SettingsTabGeneralHideCursorNever": "Nunca", "SettingsTabGeneralHideCursorOnIdle": "Ocultar cursor cuando esté inactivo", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Pueden causar inestabilidad)", - "SettingsTabSystemExpandDramSize": "Usar diseño alternativo de memoria (Desarrolladores)", + "SettingsTabSystemExpandDramSize": "Ampliar DRAM a 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorar servicios no implementados", "SettingsTabGraphics": "Gráficos", "SettingsTabGraphicsAPI": "API de gráficos", @@ -272,58 +273,58 @@ "ControllerSettingsSave": "Guardar", "ControllerSettingsClose": "Cerrar", "KeyUnknown": "Desconocido", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyShiftLeft": "Shift izquierdo", + "KeyShiftRight": "Shift Derecho", + "KeyControlLeft": "Ctrl Izquierdo", + "KeyMacControlLeft": "⌃ Izquierda", + "KeyControlRight": "Ctrl derecho", + "KeyMacControlRight": "⌃ Derecha", + "KeyAltLeft": "Alt izquierdo", + "KeyMacAltLeft": "⌥ Izquierda", + "KeyAltRight": "Alt derecho", + "KeyMacAltRight": "⌥ Derecha", + "KeyWinLeft": "⊞ Izquierda", + "KeyMacWinLeft": "⌘ Izquierda", + "KeyWinRight": "⊞ Derecha", + "KeyMacWinRight": "⌘ Derecha", + "KeyMenu": "Menú", + "KeyUp": "Arriba", + "KeyDown": "Abajo", + "KeyLeft": "Izquierda", + "KeyRight": "Derecha", "KeyEnter": "Enter", "KeyEscape": "Escape", - "KeySpace": "Space", + "KeySpace": "Espacio", "KeyTab": "Tab", - "KeyBackSpace": "Backspace", + "KeyBackSpace": "Retroceso", "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", + "KeyDelete": "Suprimir", + "KeyPageUp": "Re Pág", + "KeyPageDown": "Av Pág", + "KeyHome": "Inicio", + "KeyEnd": "Fin", + "KeyCapsLock": "Bloq Mayús", + "KeyScrollLock": "Bloq Despl", + "KeyPrintScreen": "Impr Pant", + "KeyPause": "Pausa", + "KeyNumLock": "Bloq Num", + "KeyClear": "Borrar", + "KeyKeypad0": "Numérico 0", + "KeyKeypad1": "Numérico 1", + "KeyKeypad2": "Numérico 2", + "KeyKeypad3": "Numérico 3", + "KeyKeypad4": "Numérico 4", + "KeyKeypad5": "Numérico 5", + "KeyKeypad6": "Numérico 6", + "KeyKeypad7": "Numérico 7", + "KeyKeypad8": "Numérico 8", + "KeyKeypad9": "Numérico 9", + "KeyKeypadDivide": "Numérico /", + "KeyKeypadMultiply": "Numérico *", + "KeyKeypadSubtract": "Numérico -", + "KeyKeypadAdd": "Numérico +", + "KeyKeypadDecimal": "Numérico .", + "KeyKeypadEnter": "Numérico Enter", "KeyNumber0": "0", "KeyNumber1": "1", "KeyNumber2": "2", @@ -346,32 +347,32 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "KeyUnbound": "Sin vincular", + "GamepadLeftStick": "Botón Joystick L", + "GamepadRightStick": "Botón Joystick R", + "GamepadLeftShoulder": "Botón L", + "GamepadRightShoulder": "Botón R\n", + "GamepadLeftTrigger": "Botón ZL", + "GamepadRightTrigger": "Botón ZR", + "GamepadDpadUp": "Arriba", + "GamepadDpadDown": "Abajo", + "GamepadDpadLeft": "Izquierda", + "GamepadDpadRight": "Derecha", "GamepadMinus": "-", "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "GamepadGuide": "Guía", + "GamepadMisc1": "Miscelánea", + "GamepadPaddle1": "Palanca 1", + "GamepadPaddle2": "Palanca 2", + "GamepadPaddle3": "Palanca 3", + "GamepadPaddle4": "Palanca 4", + "GamepadTouchpad": "Panel táctil", + "GamepadSingleLeftTrigger0": "Gatillo izquierdo 0", + "GamepadSingleRightTrigger0": "Gatillo derecho 0", + "GamepadSingleLeftTrigger1": "Gatillo izquierdo 1", + "GamepadSingleRightTrigger1": "Gatillo derecho 1", + "StickLeft": "Joystick izquierdo", + "StickRight": "Joystick derecho", "UserProfilesSelectedUserProfile": "Perfil de usuario seleccionado:", "UserProfilesSaveProfileName": "Guardar nombre de perfil", "UserProfilesChangeProfileImage": "Cambiar imagen de perfil", @@ -391,8 +392,8 @@ "InputDialogAddNewProfileTitle": "Introducir nombre de perfil", "InputDialogAddNewProfileHeader": "Por favor elige un nombre de usuario", "InputDialogAddNewProfileSubtext": "(Máximo de caracteres: {0})", - "AvatarChoose": "Escoger", - "AvatarSetBackgroundColor": "Establecer color de fondo", + "AvatarChoose": "Elija un Avatar", + "AvatarSetBackgroundColor": "Establecer Color de Fondo", "AvatarClose": "Cerrar", "ControllerSettingsLoadProfileToolTip": "Cargar perfil", "ControllerSettingsAddProfileToolTip": "Agregar perfil", @@ -404,13 +405,14 @@ "GameListContextMenuToggleFavorite": "Marcar favorito", "GameListContextMenuToggleFavoriteToolTip": "Marca o desmarca el juego como favorito", "SettingsTabGeneralTheme": "Tema:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "Oscuro", "SettingsTabGeneralThemeLight": "Claro", "ControllerSettingsConfigureGeneral": "Configurar", "ControllerSettingsRumble": "Vibración", "ControllerSettingsRumbleStrongMultiplier": "Multiplicador de vibraciones fuertes", "ControllerSettingsRumbleWeakMultiplier": "Multiplicador de vibraciones débiles", - "DialogMessageSaveNotAvailableMessage": "No hay datos de guardado para {0} [{1:x16}]", + "DialogMessageSaveNotAvailableMessage": "No hay datos guardados para {0} [{1:x16}]", "DialogMessageSaveNotAvailableCreateSaveMessage": "¿Quieres crear datos de guardado para este juego?", "DialogConfirmationTitle": "Ryujinx - Confirmación", "DialogUpdaterTitle": "Ryujinx - Actualizador", @@ -447,7 +449,7 @@ "DialogThemeRestartMessage": "Tema guardado. Se necesita reiniciar para aplicar el tema.", "DialogThemeRestartSubMessage": "¿Quieres reiniciar?", "DialogFirmwareInstallEmbeddedMessage": "¿Quieres instalar el firmware incluido en este juego? (Firmware versión {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", + "DialogFirmwareInstallEmbeddedSuccessMessage": "No se encontró ningún firmware instalado, pero Ryujinx pudo instalar el firmware {0} desde el juego suministrado.\nEl emulador ahora comenzará.", "DialogFirmwareNoFirmwareInstalledMessage": "No hay firmware instalado", "DialogFirmwareInstalledMessage": "Se instaló el firmware {0}", "DialogInstallFileTypesSuccessMessage": "¡Tipos de archivos instalados con éxito!", @@ -558,13 +560,13 @@ "CustomThemePathTooltip": "Carpeta que contiene los temas personalizados para la interfaz", "CustomThemeBrowseTooltip": "Busca un tema personalizado para la interfaz", "DockModeToggleTooltip": "El modo dock o modo TV hace que la consola emulada se comporte como una Nintendo Switch en su dock. Esto mejora la calidad gráfica en la mayoría de los juegos. Del mismo modo, si lo desactivas, el sistema emulado se comportará como una Nintendo Switch en modo portátil, reduciendo la cálidad de los gráficos.\n\nConfigura los controles de \"Jugador\" 1 si planeas jugar en modo dock/TV; configura los controles de \"Portátil\" si planeas jugar en modo portátil.\n\nActívalo si no sabes qué hacer.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", + "DirectKeyboardTooltip": "Acceso directo al teclado (HID). Proporciona acceso a los juegos a tu teclado como dispositivo de entrada de texto.\n\nSolo funciona con juegos que soportan nativamente el uso del teclado en la consola Switch.\n\nDeje APAGADO si no está seguro.", + "DirectMouseTooltip": "Acceso directo al ratón (HID). Proporciona acceso a los juegos a tu ratón como dispositivo apuntador.\n\nSolo funciona con juegos que soportan nativamente el uso del ratón en la consola Switch, los cuales son pocos.\n\nDeje APAGADO si no está seguro.", "RegionTooltip": "Cambia la región del sistema", "LanguageTooltip": "Cambia el idioma del sistema", "TimezoneTooltip": "Cambia la zona horaria del sistema", "TimeTooltip": "Cambia la hora del sistema", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", + "VSyncToggleTooltip": "Emula el Vertical Sync de la consola. Esencialmente es un limitante de fotogramas para la mayoria de juegos; deshabilitar esto puede hacer que los juegos corran a una velocidad incrementeda o hacer que las pantallas de carga duren mas o que se queden colgadas.\nPuede ser cambiado mientras el juego esta corriendo con una hotkey de tu preferencia (F1 por defecto). Recomendamos hacer esto si planeas en dehabilitarlo.\n\nDejar PRENDIDO si no esta seguro.", "PptcToggleTooltip": "Guarda funciones de JIT traducidas para que no sea necesario traducirlas cada vez que el juego carga.\n\nReduce los tirones y acelera significativamente el tiempo de inicio de los juegos después de haberlos ejecutado al menos una vez.\n\nActívalo si no sabes qué hacer.", "FsIntegrityToggleTooltip": "Comprueba si hay archivos corruptos en los juegos que ejecutes al abrirlos, y si detecta archivos corruptos, muestra un error de Hash en los registros.\n\nEsto no tiene impacto alguno en el rendimiento y está pensado para ayudar a resolver problemas.\n\nActívalo si no sabes qué hacer.", "AudioBackendTooltip": "Cambia el motor usado para renderizar audio.\n\nSDL2 es el preferido, mientras que OpenAL y SoundIO se usan si hay problemas con este. Dummy no produce audio.\n\nSelecciona SDL2 si no sabes qué hacer.", @@ -573,15 +575,15 @@ "MemoryManagerHostTooltip": "Mapea la memoria directamente en la dirección de espacio del host. Compilación y ejecución JIT mucho más rápida.", "MemoryManagerUnsafeTooltip": "Mapea la memoria directamente, pero no enmascara la dirección dentro del espacio de dirección del guest antes del acceso. El modo más rápido, pero a costa de seguridad. La aplicación guest puede acceder a la memoria desde cualquier parte en Ryujinx, así que ejecuta solo programas en los que confíes cuando uses este modo.", "UseHypervisorTooltip": "Usar Hypervisor en lugar de JIT. Mejora enormemente el rendimiento cuando está disponible, pero puede ser inestable en su estado actual.", - "DRamTooltip": "Expande la memoria DRAM del sistema emulado de 4GiB a 6GiB.\n\nUtilizar solo con packs de texturas HD o mods de resolución 4K. NO mejora el rendimiento.\n\nDesactívalo si no sabes qué hacer.", + "DRamTooltip": "Utiliza un modo de memoria alternativa con 8GiB de DRAM para imitar un modelo de desarrollo de conmutación.\n\nEsto solo es útil para paquetes de texturizado de alta resolución o mods de resolución 4k. NO mejora el rendimiento.\n\nDeje APAGADO si no está seguro.", "IgnoreMissingServicesTooltip": "Hack para ignorar servicios no implementados del Horizon OS. Esto puede ayudar a sobrepasar crasheos cuando inicies ciertos juegos.\n\nDesactívalo si no sabes qué hacer.", "GraphicsBackendThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio procesamiento con múltiples hilos. Rendimiento ligeramente superior en controladores gráficos que soporten múltiples hilos.\n\nSelecciona \"Auto\" si no sabes qué hacer.", "GalThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio procesamiento con múltiples hilos. Rendimiento ligeramente superior en controladores gráficos que soporten múltiples hilos.\n\nSelecciona \"Auto\" si no sabes qué hacer.", "ShaderCacheToggleTooltip": "Guarda una caché de sombreadores en disco, la cual reduce los tirones a medida que vas jugando.\n\nActívalo si no sabes qué hacer.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", + "ResolutionScaleTooltip": "Multiplica la resolución de renderizado del juego.\n\nPuede que algunos juegos no funcionen con esto y se vean pixelados incluso cuando la resolucion es incrementada; para estos juegos, puede ser que requieras de mods que remuevan el anti-aliasing o que incrementen la resolucion de renderizado interna. Para esto ultimo, probablemente querra elegir Nativo.\n\nEsta opcion puede ser cambiada mientras el juego esta en ejecucion al hacer click en el boton \"Aplicar\"; puedes simplemente move las ventana de opciones de lado y experimentar hasta que encuentres tu aparience preferida para un juego.\n\nTen en mente que 4x es un exceso para virtualmente cualquier maquina.", "ResolutionScaleEntryTooltip": "Escalado de resolución de coma flotante, como por ejemplo 1,5. Los valores no íntegros pueden causar errores gráficos o crashes.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", + "AnisotropyTooltip": "Nivel de filtrado anisotrópico. Selecciona Auto para utilizar el valor solicitado por el juego.", + "AspectRatioTooltip": "Relación de Aspecto aplicada a la ventana de renderizado.\n\nCambia esto solo si estás usando un mod de Relación de Aspecto para tu juego, de otra forma tus gráficos se verán estrechos.\n\nDéjalo en 16:9 si no estás seguro.", "ShaderDumpPathTooltip": "Directorio en el cual se volcarán los sombreadores de los gráficos", "FileLogTooltip": "Guarda los registros de la consola en archivos en disco. No afectan al rendimiento.", "StubLogTooltip": "Escribe mensajes de Stub en la consola. No afectan al rendimiento.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Abrir la guía de instalación", "NoUpdate": "No actualizado", "TitleUpdateVersionLabel": "Versión {0} - {1}", + "TitleBundledUpdateVersionLabel": "Paquete: Versión {0}", + "TitleBundledDlcLabel": "Paquete:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Confirmación", "FileDialogAllTypes": "Todos los tipos", @@ -703,7 +707,7 @@ "UserProfileWindowTitle": "Administrar perfiles de usuario", "CheatWindowTitle": "Administrar cheats", "DlcWindowTitle": "Administrar contenido descargable", - "ModWindowTitle": "Manage Mods for {0} ({1})", + "ModWindowTitle": "Administrar Mods para {0} ({1})", "UpdateWindowTitle": "Administrar actualizaciones", "CheatWindowHeading": "Cheats disponibles para {0} [{1}]", "BuildId": "Id de compilación:", @@ -723,9 +727,9 @@ "UserProfilesName": "Nombre:", "UserProfilesUserId": "Id de Usuario:", "SettingsTabGraphicsBackend": "Fondo de gráficos", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", + "SettingsTabGraphicsBackendTooltip": "Selecciona el backend de gráficos que se utilizará en el emulador.\n\nVulkan es el mejor para todas las tarjetas gráficas modernas, siempre y cuando sus controladores estén actualizados. Vulkan también presenta una compilación de shaders más rápida (menos stuttering) en todos los proveedores de GPU.\n\nOpenGL puede lograr mejores resultados en GPUs viejas de Nvidia, en GPUs viejas de AMD en Linux, o en GPUs con VRAM bajo, aunque el stutter del compilador de shaders será mayor.\n\nSelecciona Vulkan si no estás seguro. Selecciona OpenGL si tu GPU no soporta Vulkan incluso con los últimos controladores gráficos.", "SettingsEnableTextureRecompression": "Activar recompresión de texturas", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", + "SettingsEnableTextureRecompressionTooltip": "Comprime las texturas ASTC para reducir el uso de VRAM.\n\nLos juegos que usan este formato de texturas incluyen Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder y The Legend of Zelda: Tears of the Kingdom.\n\nLas tarjetas graficas con 4GBs de VRAM o menos probablemente se congelaran en algun punto mientras corran estos juegos.\n\nHabilitar solo si es que se te esta acabando el VRAM en los juegos previamente mencionados. Dejar APAGADO si no esta seguro.", "SettingsTabGraphicsPreferredGpu": "GPU preferida", "SettingsTabGraphicsPreferredGpuTooltip": "Selecciona la tarjeta gráfica que se utilizará con los back-end de gráficos Vulkan.\n\nNo afecta la GPU que utilizará OpenGL.\n\nFije a la GPU marcada como \"dGUP\" ante dudas. Si no hay una, no haga modificaciones.", "SettingsAppRequiredRestartMessage": "Reinicio de Ryujinx requerido.", @@ -751,13 +755,14 @@ "Recover": "Recuperar", "UserProfilesRecoverHeading": "Datos de guardado fueron encontrados para las siguientes cuentas", "UserProfilesRecoverEmptyList": "No hay perfiles a recuperar", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", + "GraphicsAATooltip": "Aplica anti-aliasing al renderizado del juego.\n\nFXAA probablemente dejara la imagen borrosa, mientras que SMAA intentara encontrar bordes dentados y lijarlos.\n\nNo se recomienda usarlo en conjunto con el filtro de escalado FSR.\n\nEsta opcion puede ser cambiada mientras el juego esta en ejecucion al hacer click en el boton \"Aplicar\"; puedes simplemente move las ventana de opciones de lado y experimentar hasta que encuentres tu aparience preferida para un juego.\n\nDejalo en NINGUNO si no estas seguro.", "GraphicsAALabel": "Suavizado de bordes:", "GraphicsScalingFilterLabel": "Filtro de escalado:", - "GraphicsScalingFilterTooltip": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.", + "GraphicsScalingFilterTooltip": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nEl filtro bilineal funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilineal para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso combinado con FXAA o SMAA.\n\nEscalar está recomendado bajar resoluciones más grandes que la resolución de pantalla. Se puede usar para conseguir un efecto de alisado cuando se reduce la escala más de un factor 2x.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.", "GraphicsScalingFilterBilinear": "Bilinear\n", "GraphicsScalingFilterNearest": "Cercano", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Área", "GraphicsScalingFilterLevelLabel": "Nivel", "GraphicsScalingFilterLevelTooltip": "Ajuste el nivel de nitidez FSR 1.0. Mayor es más nítido.", "SmaaLow": "SMAA Bajo", @@ -767,7 +772,7 @@ "UserEditorTitle": "Editar usuario", "UserEditorTitleCreate": "Crear Usuario", "SettingsTabNetworkInterface": "Interfaz de Red", - "NetworkInterfaceTooltip": "Interfaz de red usada para características LAN/LDN.\n\njunto con una VPN o XLink Kai y un juego con soporte LAN, puede usarse para suplantar una conexión de la misma red a través de Internet.\n\nDeje en DEFAULT si no está seguro.", + "NetworkInterfaceTooltip": "Interfaz de red usada para características LAN/LDN.\n\nJunto con una VPN o XLink Kai y un juego con soporte LAN, puede usarse para suplantar una conexión de la misma red a través de Internet.\n\nDeje en DEFAULT si no está seguro.", "NetworkInterfaceDefault": "Predeterminado", "PackagingShaders": "Empaquetando sombreadores", "AboutChangelogButton": "Ver registro de cambios en GitHub", diff --git a/src/Ryujinx/Assets/Locales/fi_FI.json b/src/Ryujinx/Assets/Locales/fi_FI.json new file mode 100644 index 000000000..b8ccb197e --- /dev/null +++ b/src/Ryujinx/Assets/Locales/fi_FI.json @@ -0,0 +1,785 @@ +{ + "Language": "Suomi", + "MenuBarFileOpenApplet": "Avaa Sovelma", + "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Avaa Mii Muokkaus Sovelma erillisessä tilassa", + "SettingsTabInputDirectMouseAccess": "Suora Hiiren Käyttö", + "SettingsTabSystemMemoryManagerMode": "Muistinhallintatila:", + "SettingsTabSystemMemoryManagerModeSoftware": "Ohjelmisto", + "SettingsTabSystemMemoryManagerModeHost": "Isäntä (nopea)", + "SettingsTabSystemMemoryManagerModeHostUnchecked": "Tarkistamaton isäntä (Nopein, epäturvallinen)", + "SettingsTabSystemUseHypervisor": "Käytä Hypervisoria", + "MenuBarFile": "_Tiedosto", + "MenuBarFileOpenFromFile": "_Lataa Sovellus Tiedostosta", + "MenuBarFileOpenFromFileError": "Valitusta tiedostosta ei löydetty sovellusta.", + "MenuBarFileOpenUnpacked": "Lataa _Pakkaamaton Peli", + "MenuBarFileOpenEmuFolder": "Avaa Ryujinx Kansio", + "MenuBarFileOpenLogsFolder": "Avaa Logs Kansio", + "MenuBarFileExit": "_Poistu", + "MenuBarOptions": "_Asetukset", + "MenuBarOptionsToggleFullscreen": "Vaihda Koko Näyttö tilaan", + "MenuBarOptionsStartGamesInFullscreen": "Aloita Pelit Koko Näyttö Tilassa", + "MenuBarOptionsStopEmulation": "Lopeta Emulointi", + "MenuBarOptionsSettings": "_Asetukset", + "MenuBarOptionsManageUserProfiles": "_Hallitse Käyttäjäprofiileja", + "MenuBarActions": "_Toiminnot", + "MenuBarOptionsSimulateWakeUpMessage": "Simuloi Herätys viesti", + "MenuBarActionsScanAmiibo": "Skannaa Amiibo", + "MenuBarTools": "_Työkalut", + "MenuBarToolsInstallFirmware": "Asenna Laiteohjelmisto", + "MenuBarFileToolsInstallFirmwareFromFile": "Asenna laiteohjelmisto XCI- tai ZIP-tiedostosta", + "MenuBarFileToolsInstallFirmwareFromDirectory": "Asenna laiteohjelmisto kansiosta", + "MenuBarToolsManageFileTypes": "Hallitse tiedostotyyppejä", + "MenuBarToolsInstallFileTypes": "Asenna tiedostotyypit", + "MenuBarToolsUninstallFileTypes": "Poista tiedostotyypit", + "MenuBarView": "_Näkymä", + "MenuBarViewWindow": "Ikkunan koko", + "MenuBarViewWindow720": "720p", + "MenuBarViewWindow1080": "1080p", + "MenuBarHelp": "_Ohje", + "MenuBarHelpCheckForUpdates": "Tarkista päivitykset", + "MenuBarHelpAbout": "Tietoja", + "MenuSearch": "Hae...", + "GameListHeaderFavorite": "Fav", + "GameListHeaderIcon": "Icon", + "GameListHeaderApplication": "Nimi", + "GameListHeaderDeveloper": "Kehittäjä", + "GameListHeaderVersion": "Versio", + "GameListHeaderTimePlayed": "Peliaika", + "GameListHeaderLastPlayed": "Viimeksi pelattu", + "GameListHeaderFileExtension": "File Ext", + "GameListHeaderFileSize": "Tiedoston koko", + "GameListHeaderPath": "Polku", + "GameListContextMenuOpenUserSaveDirectory": "Avaa Käyttäjän Tallennuskansio", + "GameListContextMenuOpenUserSaveDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen käyttäjä tallennuksen", + "GameListContextMenuOpenDeviceSaveDirectory": "Avaa Laitteen Tallennuskansio", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen laite tallennuksen", + "GameListContextMenuOpenBcatSaveDirectory": "Avaa BCAT Tallennuskansio", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen BCAT tallennuksen", + "GameListContextMenuManageTitleUpdates": "Hallitse Otsikon Päivityksiä", + "GameListContextMenuManageTitleUpdatesToolTip": "Avaa Otsikon Päivityksien hallinnan ikkunan", + "GameListContextMenuManageDlc": "Hallitse Ladattavaa Sisältöä", + "GameListContextMenuManageDlcToolTip": "Avaa Ladattavan Sisällön hallinnan ikkunan", + "GameListContextMenuCacheManagement": "Välimuistin hallinta", + "GameListContextMenuCacheManagementPurgePptc": "Jonota PPTC Uudelleenrakennus", + "GameListContextMenuCacheManagementPurgePptcToolTip": "Käynnistää PPTC uudelleenrakennuksen pelin käynnistyessä", + "GameListContextMenuCacheManagementPurgeShaderCache": "Puhdista Varjostus Lähimuisti", + "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Poista sovelluksen varjostusvälimuisti", + "GameListContextMenuCacheManagementOpenPptcDirectory": "Avaa PPTC kansio", + "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen PPTC välimuistin", + "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Avaa varjostin välimuisti kansion", + "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen varjostin lähimuistin", + "GameListContextMenuExtractData": "Pura Data", + "GameListContextMenuExtractDataExeFS": "ExeFS", + "GameListContextMenuExtractDataExeFSToolTip": "Pura ExeFS-osio sovelluksen nykyisestä kokoonpanosta (mukaan lukien päivitykset)", + "GameListContextMenuExtractDataRomFS": "RomFS", + "GameListContextMenuExtractDataRomFSToolTip": "Pura RomFS-osio sovelluksen nykyisestä kokoonpanosta (mukaan lukien päivitykset)", + "GameListContextMenuExtractDataLogo": "Logo", + "GameListContextMenuExtractDataLogoToolTip": "Pura logo sovelluksen nykyisestä kokoonpanosta (mukaan lukien päivitykset)", + "GameListContextMenuCreateShortcut": "Luo sovellus pikakuvake", + "GameListContextMenuCreateShortcutToolTip": "Luo työpöytä pikakuvake, joka käynnistään valitun sovelluksen", + "GameListContextMenuCreateShortcutToolTipMacOS": "Luo pikakuvake macOS:n sovelluskansioon, joka käynnistää valitun sovelluksen", + "GameListContextMenuOpenModsDirectory": "Avaa Mods kansio", + "GameListContextMenuOpenModsDirectoryToolTip": "Avaa kansion, joka sisältää sovelluksen Modit", + "GameListContextMenuOpenSdModsDirectory": "Avaa Atmosphere Mods kansio", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Avaa vaihtoehtoisen SD-kortin Atmosphere kansion, joka sisältää sovelluksen Modit. Hyödyllisiä modeihin, jotka on pakattu oikeisiin laitteisiin.", + "StatusBarGamesLoaded": "{0}/{1} Pelejä Ladattu", + "StatusBarSystemVersion": "Järjestelmäversio: {0}", + "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", + "LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}", + "LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.", + "LinuxVmMaxMapCountDialogButtonUntilRestart": "Kyllä, seuraavaan uudelleenkäynnistykseen asti", + "LinuxVmMaxMapCountDialogButtonPersistent": "Kyllä, pysyvästi", + "LinuxVmMaxMapCountWarningTextPrimary": "Max amount of memory mappings is lower than recommended.", + "LinuxVmMaxMapCountWarningTextSecondary": "The current value of vm.max_map_count ({0}) is lower than {1}. Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.\n\nYou might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that.", + "Settings": "Asetukset", + "SettingsTabGeneral": "Käyttöliittymä", + "SettingsTabGeneralGeneral": "Yleinen", + "SettingsTabGeneralEnableDiscordRichPresence": "Ota Discord Rich Presence käyttöön", + "SettingsTabGeneralCheckUpdatesOnLaunch": "Tarkista päivitykset käynnistyksen yhdeydessä", + "SettingsTabGeneralShowConfirmExitDialog": "Näytä \"Vahvista Poistuminen\"-ikkuna", + "SettingsTabGeneralRememberWindowState": "Muista Ikkunan Koko/Sijainti", + "SettingsTabGeneralHideCursor": "Piilota kursori:", + "SettingsTabGeneralHideCursorNever": "Ei koskaan", + "SettingsTabGeneralHideCursorOnIdle": "Käyttämättä", + "SettingsTabGeneralHideCursorAlways": "Aina", + "SettingsTabGeneralGameDirectories": "Pelikansiot", + "SettingsTabGeneralAdd": "Lisää", + "SettingsTabGeneralRemove": "Poista", + "SettingsTabSystem": "Järjestelmä", + "SettingsTabSystemCore": "Ydin", + "SettingsTabSystemSystemRegion": "Järjestelmän alue:", + "SettingsTabSystemSystemRegionJapan": "Japani", + "SettingsTabSystemSystemRegionUSA": "Yhdysvallat", + "SettingsTabSystemSystemRegionEurope": "Eurooppa", + "SettingsTabSystemSystemRegionAustralia": "Australia", + "SettingsTabSystemSystemRegionChina": "Kiina", + "SettingsTabSystemSystemRegionKorea": "Korea", + "SettingsTabSystemSystemRegionTaiwan": "Taiwan", + "SettingsTabSystemSystemLanguage": "Järjestelmän kieli:", + "SettingsTabSystemSystemLanguageJapanese": "Japani", + "SettingsTabSystemSystemLanguageAmericanEnglish": "Amerikanenglanti", + "SettingsTabSystemSystemLanguageFrench": "Ranska", + "SettingsTabSystemSystemLanguageGerman": "Saksa", + "SettingsTabSystemSystemLanguageItalian": "Italia", + "SettingsTabSystemSystemLanguageSpanish": "Espanja", + "SettingsTabSystemSystemLanguageChinese": "Kiina", + "SettingsTabSystemSystemLanguageKorean": "Korea", + "SettingsTabSystemSystemLanguageDutch": "Hollanti", + "SettingsTabSystemSystemLanguagePortuguese": "Portugali", + "SettingsTabSystemSystemLanguageRussian": "Venäjä", + "SettingsTabSystemSystemLanguageTaiwanese": "Taiwani", + "SettingsTabSystemSystemLanguageBritishEnglish": "Britannianenglanti", + "SettingsTabSystemSystemLanguageCanadianFrench": "Kanadanranska", + "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Amerikanespanja", + "SettingsTabSystemSystemLanguageSimplifiedChinese": "Yksinkertaistettu Kiina", + "SettingsTabSystemSystemLanguageTraditionalChinese": "Perinteinen Kiina", + "SettingsTabSystemSystemTimeZone": "Järjestelmän aikavyöhyke:", + "SettingsTabSystemSystemTime": "Järjestelmän aika:", + "SettingsTabSystemEnableVsync": "VSync", + "SettingsTabSystemEnablePptc": "PPTC (Profilioitu Pysyvä Käännösvälimuisti)", + "SettingsTabSystemEnableFsIntegrityChecks": "Tiedostojärjestelmän eheys tarkistukset", + "SettingsTabSystemAudioBackend": "Äänen taustaohjelma:", + "SettingsTabSystemAudioBackendDummy": "Nukke", + "SettingsTabSystemAudioBackendOpenAL": "OpenAL", + "SettingsTabSystemAudioBackendSoundIO": "SoundIO", + "SettingsTabSystemAudioBackendSDL2": "SDL2", + "SettingsTabSystemHacks": "Hacks", + "SettingsTabSystemHacksNote": "Voi aiheuttaa epävakautta", + "SettingsTabSystemExpandDramSize": "Laajenna DRAM 8GiB:iin", + "SettingsTabSystemIgnoreMissingServices": "Ohita Puuttuvat Palvelut", + "SettingsTabGraphics": "Grafiikka", + "SettingsTabGraphicsAPI": "Grafiikka API", + "SettingsTabGraphicsEnableShaderCache": "Ota varjostus lähimuisti käyttöön", + "SettingsTabGraphicsAnisotropicFiltering": "Anisotrooppinen suodatus:", + "SettingsTabGraphicsAnisotropicFilteringAuto": "Autom.", + "SettingsTabGraphicsAnisotropicFiltering2x": "2x", + "SettingsTabGraphicsAnisotropicFiltering4x": "4x", + "SettingsTabGraphicsAnisotropicFiltering8x": "8x", + "SettingsTabGraphicsAnisotropicFiltering16x": "16x", + "SettingsTabGraphicsResolutionScale": "Resoluutioasteikko:", + "SettingsTabGraphicsResolutionScaleCustom": "Mukautettu (Ei suositeltu)", + "SettingsTabGraphicsResolutionScaleNative": "Natiivi (720p/1080p)", + "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", + "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", + "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Ei suositeltu)", + "SettingsTabGraphicsAspectRatio": "Kuvasuhde:", + "SettingsTabGraphicsAspectRatio4x3": "4:3", + "SettingsTabGraphicsAspectRatio16x9": "16:9", + "SettingsTabGraphicsAspectRatio16x10": "16:10", + "SettingsTabGraphicsAspectRatio21x9": "21:9", + "SettingsTabGraphicsAspectRatio32x9": "32:9", + "SettingsTabGraphicsAspectRatioStretch": "Venytä sopimaan ikkunaan", + "SettingsTabGraphicsDeveloperOptions": "Kehittäjäasetukset", + "SettingsTabGraphicsShaderDumpPath": "Graphics Shader Dump Path:", + "SettingsTabLogging": "Lokit", + "SettingsTabLoggingLogging": "Lokit", + "SettingsTabLoggingEnableLoggingToFile": "Ota lokitiedostoon kirjaaminen käyttöön", + "SettingsTabLoggingEnableStubLogs": "Ota Stub lokit käyttöön", + "SettingsTabLoggingEnableInfoLogs": "Ota Info lokit käyttöön", + "SettingsTabLoggingEnableWarningLogs": "Ota Varoitus lokit käyttöön", + "SettingsTabLoggingEnableErrorLogs": "Ota Virhe lokit käyttöön", + "SettingsTabLoggingEnableTraceLogs": "Ota Trace lokit käyttöön", + "SettingsTabLoggingEnableGuestLogs": "Ota Guest lokit käyttöön", + "SettingsTabLoggingEnableFsAccessLogs": "Ota Fs lokit käyttöön", + "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:", + "SettingsTabLoggingDeveloperOptions": "Kehittäjäasetukset", + "SettingsTabLoggingDeveloperOptionsNote": "VAROITUS: Vähentää suorituskykyä", + "SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:", + "SettingsTabLoggingGraphicsBackendLogLevelNone": "Ei mitään", + "SettingsTabLoggingGraphicsBackendLogLevelError": "Virheet", + "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Hidastukset", + "SettingsTabLoggingGraphicsBackendLogLevelAll": "Kaikki", + "SettingsTabLoggingEnableDebugLogs": "Ota Debugt lokit käyttöön", + "SettingsTabInput": "Ohjaus", + "SettingsTabInputEnableDockedMode": "Telakoitu-tila", + "SettingsTabInputDirectKeyboardAccess": "Suora Näppäimistön Käyttö", + "SettingsButtonSave": "Tallenna", + "SettingsButtonClose": "Sulje", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Peruuta", + "SettingsButtonApply": "Käytä", + "ControllerSettingsPlayer": "Pelaaja", + "ControllerSettingsPlayer1": "Pelaaja 1", + "ControllerSettingsPlayer2": "Pelaaja 2", + "ControllerSettingsPlayer3": "Pelaaja 3", + "ControllerSettingsPlayer4": "Pelaaja 4", + "ControllerSettingsPlayer5": "Pelaaja 5", + "ControllerSettingsPlayer6": "Pelaaja 6", + "ControllerSettingsPlayer7": "Pelaaja 7", + "ControllerSettingsPlayer8": "Pelaaja 8", + "ControllerSettingsHandheld": "Kannettava", + "ControllerSettingsInputDevice": "Syöttölaite", + "ControllerSettingsRefresh": "Päivitä", + "ControllerSettingsDeviceDisabled": "Pois käytöstä", + "ControllerSettingsControllerType": "Ohjaintyyppi", + "ControllerSettingsControllerTypeHandheld": "Kannettava", + "ControllerSettingsControllerTypeProController": "Pro Controller", + "ControllerSettingsControllerTypeJoyConPair": "JoyCon Pari", + "ControllerSettingsControllerTypeJoyConLeft": "Vasen JoyCon", + "ControllerSettingsControllerTypeJoyConRight": "Oikea JoyCon", + "ControllerSettingsProfile": "Profiili", + "ControllerSettingsProfileDefault": "Oletus", + "ControllerSettingsLoad": "Lataa", + "ControllerSettingsAdd": "Lisää", + "ControllerSettingsRemove": "Poista", + "ControllerSettingsButtons": "Painikkeet", + "ControllerSettingsButtonA": "A", + "ControllerSettingsButtonB": "B", + "ControllerSettingsButtonX": "X", + "ControllerSettingsButtonY": "Y", + "ControllerSettingsButtonPlus": "+", + "ControllerSettingsButtonMinus": "-", + "ControllerSettingsDPad": "Ristiohjain", + "ControllerSettingsDPadUp": "Ylös", + "ControllerSettingsDPadDown": "Alas", + "ControllerSettingsDPadLeft": "Vasen", + "ControllerSettingsDPadRight": "Oikea", + "ControllerSettingsStickButton": "Painike", + "ControllerSettingsStickUp": "Ylös", + "ControllerSettingsStickDown": "Alas", + "ControllerSettingsStickLeft": "Vasen", + "ControllerSettingsStickRight": "Oikea", + "ControllerSettingsStickStick": "Sauva", + "ControllerSettingsStickInvertXAxis": "Käännä Sauvan X-akseli", + "ControllerSettingsStickInvertYAxis": "Käännä Sauvan Y-akseli", + "ControllerSettingsStickDeadzone": "Katvealue:", + "ControllerSettingsLStick": "Vasen ohjainsauva", + "ControllerSettingsRStick": "Oikea ohjainsauva", + "ControllerSettingsTriggersLeft": "Triggers Left", + "ControllerSettingsTriggersRight": "Triggers Right", + "ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left", + "ControllerSettingsTriggersButtonsRight": "Trigger Buttons Right", + "ControllerSettingsTriggers": "Liipaisimet", + "ControllerSettingsTriggerL": "L", + "ControllerSettingsTriggerR": "R", + "ControllerSettingsTriggerZL": "ZL", + "ControllerSettingsTriggerZR": "ZR", + "ControllerSettingsLeftSL": "SL", + "ControllerSettingsLeftSR": "SR", + "ControllerSettingsRightSL": "SL", + "ControllerSettingsRightSR": "SR", + "ControllerSettingsExtraButtonsLeft": "Buttons Left", + "ControllerSettingsExtraButtonsRight": "Buttons Right", + "ControllerSettingsMisc": "Sekalaiset", + "ControllerSettingsTriggerThreshold": "Liipaisinkynnys:", + "ControllerSettingsMotion": "Likke", + "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Käytä CemuHook-yhteensopivaa liikettä", + "ControllerSettingsMotionControllerSlot": "Ohjaimen paikka:", + "ControllerSettingsMotionMirrorInput": "Peilaa syöte", + "ControllerSettingsMotionRightJoyConSlot": "Vasen JoyCon paikka:", + "ControllerSettingsMotionServerHost": "Palvelinisäntä:", + "ControllerSettingsMotionGyroSensitivity": "Gyroskoopin herkkyys:", + "ControllerSettingsMotionGyroDeadzone": "Gyroskoopin katvealue:", + "ControllerSettingsSave": "Tallenna", + "ControllerSettingsClose": "Sulje", + "KeyUnknown": "Tuntematon", + "KeyShiftLeft": "Shift Left", + "KeyShiftRight": "Shift Right", + "KeyControlLeft": "Ctrl Left", + "KeyMacControlLeft": "⌃ Left", + "KeyControlRight": "Ctrl Right", + "KeyMacControlRight": "⌃ Right", + "KeyAltLeft": "Alt Left", + "KeyMacAltLeft": "⌥ Left", + "KeyAltRight": "Alt Right", + "KeyMacAltRight": "⌥ Right", + "KeyWinLeft": "⊞ Left", + "KeyMacWinLeft": "⌘ Left", + "KeyWinRight": "⊞ Right", + "KeyMacWinRight": "⌘ Right", + "KeyMenu": "Menu", + "KeyUp": "Up", + "KeyDown": "Down", + "KeyLeft": "Left", + "KeyRight": "Right", + "KeyEnter": "Enter", + "KeyEscape": "Escape", + "KeySpace": "Space", + "KeyTab": "Tab", + "KeyBackSpace": "Backspace", + "KeyInsert": "Insert", + "KeyDelete": "Delete", + "KeyPageUp": "Page Up", + "KeyPageDown": "Page Down", + "KeyHome": "Home", + "KeyEnd": "End", + "KeyCapsLock": "Caps Lock", + "KeyScrollLock": "Scroll Lock", + "KeyPrintScreen": "Print Screen", + "KeyPause": "Pause", + "KeyNumLock": "Num Lock", + "KeyClear": "Clear", + "KeyKeypad0": "Keypad 0", + "KeyKeypad1": "Keypad 1", + "KeyKeypad2": "Keypad 2", + "KeyKeypad3": "Keypad 3", + "KeyKeypad4": "Keypad 4", + "KeyKeypad5": "Keypad 5", + "KeyKeypad6": "Keypad 6", + "KeyKeypad7": "Keypad 7", + "KeyKeypad8": "Keypad 8", + "KeyKeypad9": "Keypad 9", + "KeyKeypadDivide": "Keypad Divide", + "KeyKeypadMultiply": "Keypad Multiply", + "KeyKeypadSubtract": "Keypad Subtract", + "KeyKeypadAdd": "Keypad Add", + "KeyKeypadDecimal": "Keypad Decimal", + "KeyKeypadEnter": "Keypad Enter", + "KeyNumber0": "0", + "KeyNumber1": "1", + "KeyNumber2": "2", + "KeyNumber3": "3", + "KeyNumber4": "4", + "KeyNumber5": "5", + "KeyNumber6": "6", + "KeyNumber7": "7", + "KeyNumber8": "8", + "KeyNumber9": "9", + "KeyTilde": "~", + "KeyGrave": "`", + "KeyMinus": "-", + "KeyPlus": "+", + "KeyBracketLeft": "[", + "KeyBracketRight": "]", + "KeySemicolon": ";", + "KeyQuote": "\"", + "KeyComma": ",", + "KeyPeriod": ".", + "KeySlash": "/", + "KeyBackSlash": "\\", + "KeyUnbound": "Unbound", + "GamepadLeftStick": "L Stick Button", + "GamepadRightStick": "R Stick Button", + "GamepadLeftShoulder": "Left Shoulder", + "GamepadRightShoulder": "Right Shoulder", + "GamepadLeftTrigger": "Left Trigger", + "GamepadRightTrigger": "Right Trigger", + "GamepadDpadUp": "Up", + "GamepadDpadDown": "Down", + "GamepadDpadLeft": "Left", + "GamepadDpadRight": "Right", + "GamepadMinus": "-", + "GamepadPlus": "+", + "GamepadGuide": "Guide", + "GamepadMisc1": "Misc", + "GamepadPaddle1": "Paddle 1", + "GamepadPaddle2": "Paddle 2", + "GamepadPaddle3": "Paddle 3", + "GamepadPaddle4": "Paddle 4", + "GamepadTouchpad": "Touchpad", + "GamepadSingleLeftTrigger0": "Left Trigger 0", + "GamepadSingleRightTrigger0": "Right Trigger 0", + "GamepadSingleLeftTrigger1": "Left Trigger 1", + "GamepadSingleRightTrigger1": "Right Trigger 1", + "StickLeft": "Left Stick", + "StickRight": "Right Stick", + "UserProfilesSelectedUserProfile": "Valittu käyttäjäprofiili:", + "UserProfilesSaveProfileName": "Tallenna profiilinimi", + "UserProfilesChangeProfileImage": "Vaihda profiilikuva", + "UserProfilesAvailableUserProfiles": "Saatavilla olevat käyttäjäprofiilit:", + "UserProfilesAddNewProfile": "Luo profiili", + "UserProfilesDelete": "Poista", + "UserProfilesClose": "Sulje", + "ProfileNameSelectionWatermark": "Valitse käyttäjänimi", + "ProfileImageSelectionTitle": "Profiilikuvan valinta", + "ProfileImageSelectionHeader": "Valitse profiilikuva", + "ProfileImageSelectionNote": "Voit tuoda oman profiilikuvan, tai valita yhden järjestelmästä", + "ProfileImageSelectionImportImage": "Tuo kuvatiedosto", + "ProfileImageSelectionSelectAvatar": "Valitse järjestelmä profiilikuva", + "InputDialogTitle": "Input Dialog", + "InputDialogOk": "OK", + "InputDialogCancel": "Peruuta", + "InputDialogAddNewProfileTitle": "Valitse profiilin nimi", + "InputDialogAddNewProfileHeader": "Syötä profiilin nimi", + "InputDialogAddNewProfileSubtext": "(Maksimi pituus: {0})", + "AvatarChoose": "Valitse profiilikuva", + "AvatarSetBackgroundColor": "Aseta taustaväri", + "AvatarClose": "Sulje", + "ControllerSettingsLoadProfileToolTip": "Lataa profiili", + "ControllerSettingsAddProfileToolTip": "Lisää profiili", + "ControllerSettingsRemoveProfileToolTip": "Poista profiili", + "ControllerSettingsSaveProfileToolTip": "Tallenna profiili", + "MenuBarFileToolsTakeScreenshot": "Ota kuvankaappaus", + "MenuBarFileToolsHideUi": "Piilota käyttöliittymä", + "GameListContextMenuRunApplication": "Suorita sovellus", + "GameListContextMenuToggleFavorite": "Lisää/poista suosikki", + "GameListContextMenuToggleFavoriteToolTip": "Vaihda pelin suosikkitila", + "SettingsTabGeneralTheme": "Teema:", + "SettingsTabGeneralThemeAuto": "Autom.", + "SettingsTabGeneralThemeDark": "Tumma", + "SettingsTabGeneralThemeLight": "Vaalea", + "ControllerSettingsConfigureGeneral": "Konfiguroi", + "ControllerSettingsRumble": "Tärinä", + "ControllerSettingsRumbleStrongMultiplier": "Vahva tärinä kerroin", + "ControllerSettingsRumbleWeakMultiplier": "Heikko tärinä kerroin", + "DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}]:lle ei ole tallennusta", + "DialogMessageSaveNotAvailableCreateSaveMessage": "Haluatko luoda tallenuksen tälle pelille?", + "DialogConfirmationTitle": "Ryujinx - Vahvistus", + "DialogUpdaterTitle": "Ryujinx - Päivittäjä", + "DialogErrorTitle": "Ryujinx - Virhe", + "DialogWarningTitle": "Ryujinx - Varoitus", + "DialogExitTitle": "Ryujinx - Poistu", + "DialogErrorMessage": "Ryujinx on kohdannut virheen", + "DialogExitMessage": "Oletko varma, että haluat sulkea Ryujinxin?", + "DialogExitSubMessage": "Kaikki tallentamattomat tiedot menetetään!", + "DialogMessageCreateSaveErrorMessage": "Määritetyn tallennuksen luomisessa tapahtui virhe: {0}", + "DialogMessageFindSaveErrorMessage": "Määritetyn tallennuksen löytämisessä tapahtui virhe: {0}", + "FolderDialogExtractTitle": "Valitse kansio johon purkaa", + "DialogNcaExtractionMessage": "Puretaan {0}-osiota {1}...", + "DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor", + "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraction failure. The main NCA was not present in the selected file.", + "DialogNcaExtractionCheckLogErrorMessage": "Purkamisvirhe. Lue lokitiedosto saadaksesi lisätietoja.", + "DialogNcaExtractionSuccessMessage": "Purkaminen suoritettu onnistuneesti.", + "DialogUpdaterConvertFailedMessage": "Nykyisen Ryujinx-version muuntamine epäonnistui.", + "DialogUpdaterCancelUpdateMessage": "Peruutetaan päivitys!", + "DialogUpdaterAlreadyOnLatestVersionMessage": "Käytät jo uusinta versiota Ryujinxistä!", + "DialogUpdaterFailedToGetVersionMessage": "Virhe tapahtui yrittäessä saada julkaisutietoja GitHubista. Tämä voi tapahtua, jos GitHub Actions on kokoamassa uutta julkaisua. Yritä uudelleen muutaman minuutin kuluttua.", + "DialogUpdaterConvertFailedGithubMessage": "Vastaanotetun Ryujinx-version muuntaminen Github-julkaisusta epäonnistui.", + "DialogUpdaterDownloadingMessage": "Ladataan päivitystä...", + "DialogUpdaterExtractionMessage": "Puretaan päivitystä...", + "DialogUpdaterRenamingMessage": "Uudelleennimetään päivitystä...", + "DialogUpdaterAddingFilesMessage": "Lisätään uusi päivitys...", + "DialogUpdaterCompleteMessage": "Päivitys valmis!", + "DialogUpdaterRestartMessage": "Haluatko käynnistään Ryujinxin uudelleen?", + "DialogUpdaterNoInternetMessage": "Et ole yhdistetty internettiin!", + "DialogUpdaterNoInternetSubMessage": "Varmista, että sinulla on toimiva Internet-yhteys!", + "DialogUpdaterDirtyBuildMessage": "Et voi päivittää likaista Ryujinx versioita!", + "DialogUpdaterDirtyBuildSubMessage": "Lataa Ryujinx osoitteesta https://ryujinx.org/ jos etsit tuettua versioita.", + "DialogRestartRequiredMessage": "Uudelleenkäynnistys vaaditaan", + "DialogThemeRestartMessage": "Teema on tallennettu. Teeman asettamiseen vaaditaan uudelleenkäynnistys.", + "DialogThemeRestartSubMessage": "Haluatko käynnistää uudelleen", + "DialogFirmwareInstallEmbeddedMessage": "Would you like to install the firmware embedded in this game? (Firmware {0})", + "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", + "DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed", + "DialogFirmwareInstalledMessage": "Firmware {0} was installed", + "DialogInstallFileTypesSuccessMessage": "Successfully installed file types!", + "DialogInstallFileTypesErrorMessage": "Failed to install file types.", + "DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!", + "DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.", + "DialogOpenSettingsWindowLabel": "Open Settings Window", + "DialogControllerAppletTitle": "Controller Applet", + "DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}", + "DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}", + "DialogErrorAppletErrorExceptionMessage": "Error displaying ErrorApplet Dialog: {0}", + "DialogUserErrorDialogMessage": "{0}: {1}", + "DialogUserErrorDialogInfoMessage": "\nFor more information on how to fix this error, follow our Setup Guide.", + "DialogUserErrorDialogTitle": "Ryujinx Error ({0})", + "DialogAmiiboApiTitle": "Amiibo API", + "DialogAmiiboApiFailFetchMessage": "An error occured while fetching information from the API.", + "DialogAmiiboApiConnectErrorMessage": "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.", + "DialogProfileInvalidProfileErrorMessage": "Profile {0} is incompatible with the current input configuration system.", + "DialogProfileDefaultProfileOverwriteErrorMessage": "Default Profile can not be overwritten", + "DialogProfileDeleteProfileTitle": "Deleting Profile", + "DialogProfileDeleteProfileMessage": "This action is irreversible, are you sure you want to continue?", + "DialogWarning": "Warning", + "DialogPPTCDeletionMessage": "You are about to queue a PPTC rebuild on the next boot of:\n\n{0}\n\nAre you sure you want to proceed?", + "DialogPPTCDeletionErrorMessage": "Error purging PPTC cache at {0}: {1}", + "DialogShaderDeletionMessage": "You are about to delete the Shader cache for :\n\n{0}\n\nAre you sure you want to proceed?", + "DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}", + "DialogRyujinxErrorMessage": "Ryujinx has encountered an error", + "DialogInvalidTitleIdErrorMessage": "UI error: The selected game did not have a valid title ID", + "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "A valid system firmware was not found in {0}.", + "DialogFirmwareInstallerFirmwareInstallTitle": "Install Firmware {0}", + "DialogFirmwareInstallerFirmwareInstallMessage": "System version {0} will be installed.", + "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nThis will replace the current system version {0}.", + "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nDo you want to continue?", + "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installing firmware...", + "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "System version {0} successfully installed.", + "DialogUserProfileDeletionWarningMessage": "There would be no other profiles to be opened if selected profile is deleted", + "DialogUserProfileDeletionConfirmMessage": "Do you want to delete the selected profile", + "DialogUserProfileUnsavedChangesTitle": "Warning - Unsaved Changes", + "DialogUserProfileUnsavedChangesMessage": "You have made changes to this user profile that have not been saved.", + "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", + "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", + "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", + "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", + "DialogModAlreadyExistsMessage": "Mod already exists", + "DialogModInvalidMessage": "Annettu kansio ei sisällä modia!", + "DialogModDeleteNoParentMessage": "Poistaminen epäonnistui: Ylähakemistoa ei löydetty modille \"{0}\"!", + "DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!", + "DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.", + "DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", + "DialogPerformanceCheckShaderDumpEnabledMessage": "You have shader dumping enabled, which is designed to be used by developers only.", + "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", + "DialogLoadAppGameAlreadyLoadedMessage": "A game has already been loaded", + "DialogLoadAppGameAlreadyLoadedSubMessage": "Please stop emulation or close the emulator before launching another game.", + "DialogUpdateAddUpdateErrorMessage": "The specified file does not contain an update for the selected title!", + "DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading", + "DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.", + "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", + "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", + "SettingsTabGraphicsFeaturesOptions": "Features", + "SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:", + "CommonAuto": "Auto", + "CommonOff": "Off", + "CommonOn": "On", + "InputDialogYes": "Yes", + "InputDialogNo": "No", + "DialogProfileInvalidProfileNameErrorMessage": "The file name contains invalid characters. Please try again.", + "MenuBarOptionsPauseEmulation": "Pause", + "MenuBarOptionsResumeEmulation": "Resume", + "AboutUrlTooltipMessage": "Click to open the Ryujinx website in your default browser.", + "AboutDisclaimerMessage": "Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.", + "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.", + "AboutPatreonUrlTooltipMessage": "Click to open the Ryujinx Patreon page in your default browser.", + "AboutGithubUrlTooltipMessage": "Click to open the Ryujinx GitHub page in your default browser.", + "AboutDiscordUrlTooltipMessage": "Click to open an invite to the Ryujinx Discord server in your default browser.", + "AboutTwitterUrlTooltipMessage": "Click to open the Ryujinx Twitter page in your default browser.", + "AboutRyujinxAboutTitle": "Tietoja:", + "AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nPlease support us on Patreon.\nGet all the latest news on our Twitter or Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.", + "AboutRyujinxMaintainersTitle": "Ylläpitäjä:", + "AboutRyujinxMaintainersContentTooltipMessage": "Click to open the Contributors page in your default browser.", + "AboutRyujinxSupprtersTitle": "Supported on Patreon By:", + "AmiiboSeriesLabel": "Amiibo-sarja", + "AmiiboCharacterLabel": "Hahmo", + "AmiiboScanButtonLabel": "Skannaa se", + "AmiiboOptionsShowAllLabel": "Näytä kaikki Amiibot", + "AmiiboOptionsUsRandomTagLabel": "Hack: Use Random tag Uuid", + "DlcManagerTableHeadingEnabledLabel": "Käytössä", + "DlcManagerTableHeadingTitleIdLabel": "Title ID", + "DlcManagerTableHeadingContainerPathLabel": "Container Path", + "DlcManagerTableHeadingFullPathLabel": "Koko polku", + "DlcManagerRemoveAllButton": "Poista kaikki", + "DlcManagerEnableAllButton": "Ota kaikki käyttöön", + "DlcManagerDisableAllButton": "Poista kaikki käytöstä", + "ModManagerDeleteAllButton": "Poista kaikki", + "MenuBarOptionsChangeLanguage": "Vaihda kieli", + "MenuBarShowFileTypes": "Näytä tiedostotyypit", + "CommonSort": "Lajittele", + "CommonShowNames": "Näytä nimet", + "CommonFavorite": "Favorite", + "OrderAscending": "Nouseva", + "OrderDescending": "Laskeva", + "SettingsTabGraphicsFeatures": "Ominaisuudet & parannukset", + "ErrorWindowTitle": "Virhe ikkuna", + "ToggleDiscordTooltip": "Choose whether or not to display Ryujinx on your \"currently playing\" Discord activity", + "AddGameDirBoxTooltip": "Syötä pelihakemisto lisätäksesi sen luetteloon", + "AddGameDirTooltip": "Lisää pelihakemisto luetteloon", + "RemoveGameDirTooltip": "Poista valittu pelihakemisto", + "CustomThemeCheckTooltip": "Use a custom Avalonia theme for the GUI to change the appearance of the emulator menus", + "CustomThemePathTooltip": "Path to custom GUI theme", + "CustomThemeBrowseTooltip": "Browse for a custom GUI theme", + "DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.", + "DirectKeyboardTooltip": "Suoran näppäimistön käyttö (HID) tuki. Tarjoaa peleille suoran käytön näppäimistöön tekstinsyöttö laitteena.\n\nToimii vain pelien kanssa, jotka tukevat natiivisti näppäimistön käyttöä Switch laitteistossa.\n\nJätä pois päältä jos olet epävarma.", + "DirectMouseTooltip": "Suoran hiiren käyttö (HID) tuki. Tarjoaa peleille käytön hiireen osoittimena.\n\nToimii vain pelien kanssa, jotka natiivisti tukevat hiiren käyttöä Switch laitteistossa, jotka ovat harvoja.\n\nKun käytössä, kosketusnäyttö ei välttämättä toimi.\n\nJätä pois päältä jos olet epävarma.", + "RegionTooltip": "Muuta järjestelmän aluetta", + "LanguageTooltip": "Muuta järjestelmän kieltä", + "TimezoneTooltip": "Muuta järjestelmän aikavyöhykettä", + "TimeTooltip": "Muuta järjestelmän aikaa", + "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", + "PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", + "FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.", + "AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", + "MemoryManagerTooltip": "Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance.\n\nSet to HOST UNCHECKED if unsure.", + "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", + "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", + "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", + "UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", + "IgnoreMissingServicesTooltip": "Ohittaa toteuttamattomat Horizon OS-pavelut. Tämä saattaa auttaa ohittamaan kaatumisia käynnistäessä tiettyjä pelejä.\n\nJätä pois päältä, jos olet epävarma.", + "GraphicsBackendThreadingTooltip": "Suorittaa grafiikka taustajärjestelmän komennot toisella säikeellä.\n\nNopeuttaa varjostuksen kääntämistä, vähentää pätkimistä, ja parantaa suorityskykyä näytönohjain ajureilla, joissa ei ole omaa monisäkeistystukea.\nHieman parempi suorituskyky ajureilla, joissa on monisäkeistystuki.\n\nAseta AUTO:ksi, jos olet epävarma.", + "GalThreadingTooltip": "Suorittaa grafiikka taustajärjestelmän komennot toisella säikeellä.\n\nNopeuttaa varjostuksen kääntämistä, vähentää pätkimistä, ja parantaa suorityskykyä näytönohjain ajureilla, joissa ei ole omaa monisäkeistystukea.\nHieman parempi suorituskyky ajureilla, joissa on monisäkeistystuki.\n\nAseta AUTO:ksi, jos olet epävarma.", + "ShaderCacheToggleTooltip": "Tallentaa varjostin välimuistin levylle, joka vähentää pätkimistä myöhemmin.\n\nJätä päälle, jos olet epävarma.", + "ResolutionScaleTooltip": "Moninkertaistaa pelin renderöintiresoluution.\n\nMuutama peli ei välttämättä toimi tämän kanssa ja näyttää pikselöidyltä, vaikka resoluution on suurennettu. Näille peleille, saatat joutua etsimään modin joka poistaa anti-aliasoinnin tai joka nostaa pelin sisäistä renderöintiresoluutiota. Jos käytät jälkimmäistä, haluat todennäköisesti valita Natiivin.\n\nTämän asetuksen voi vaihtaa samalla kun peli on päällä painamalla \"Käytä\". Voit siirtää asetus ikkunan sivuun ja kokeilla, kunnes löydät mielisen resoluution pelille.\n\nPidä mielessä, että 4x menee yli käytännöllisesti kaikelle laitteistolle.", + "ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.", + "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", + "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", + "ShaderDumpPathTooltip": "Graphics Shaders Dump Path", + "FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.", + "StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.", + "InfoLogTooltip": "Prints info log messages in the console. Does not affect performance.", + "WarnLogTooltip": "Prints warning log messages in the console. Does not affect performance.", + "ErrorLogTooltip": "Prints error log messages in the console. Does not affect performance.", + "TraceLogTooltip": "Prints trace log messages in the console. Does not affect performance.", + "GuestLogTooltip": "Prints guest log messages in the console. Does not affect performance.", + "FileAccessLogTooltip": "Prints file access log messages in the console.", + "FSAccessLogModeTooltip": "Enables FS access log output to the console. Possible modes are 0-3", + "DeveloperOptionTooltip": "Käytä varovasti", + "OpenGlLogLevel": "Requires appropriate log levels enabled", + "DebugLogTooltip": "Prints debug log messages in the console.\n\nOnly use this if specifically instructed by a staff member, as it will make logs difficult to read and worsen emulator performance.", + "LoadApplicationFileTooltip": "Open a file explorer to choose a Switch compatible file to load", + "LoadApplicationFolderTooltip": "Open a file explorer to choose a Switch compatible, unpacked application to load", + "OpenRyujinxFolderTooltip": "Open Ryujinx filesystem folder", + "OpenRyujinxLogsTooltip": "Opens the folder where logs are written to", + "ExitTooltip": "Sulje Ryujinx", + "OpenSettingsTooltip": "Avaa asetusikkuna", + "OpenProfileManagerTooltip": "Open User Profiles Manager window", + "StopEmulationTooltip": "Stop emulation of the current game and return to game selection", + "CheckUpdatesTooltip": "Check for updates to Ryujinx", + "OpenAboutTooltip": "Open About Window", + "GridSize": "Grid Size", + "GridSizeTooltip": "Change the size of grid items", + "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brazilian Portuguese", + "AboutRyujinxContributorsButtonHeader": "See All Contributors", + "SettingsTabSystemAudioVolume": "Äänenvoimakkuus:", + "AudioVolumeTooltip": "Vaihda äänenvoimakkuutta", + "SettingsTabSystemEnableInternetAccess": "Guest Internet Access/LAN Mode", + "EnableInternetAccessTooltip": "Allows the emulated application to connect to the Internet.\n\nGames with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well.\n\nDoes NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet.\n\nLeave OFF if unsure.", + "GameListContextMenuManageCheatToolTip": "Manage Cheats", + "GameListContextMenuManageCheat": "Manage Cheats", + "GameListContextMenuManageModToolTip": "Manage Mods", + "GameListContextMenuManageMod": "Manage Mods", + "ControllerSettingsStickRange": "Range:", + "DialogStopEmulationTitle": "Ryujinx - Stop Emulation", + "DialogStopEmulationMessage": "Are you sure you want to stop emulation?", + "SettingsTabCpu": "Prosessori", + "SettingsTabAudio": "Ääni", + "SettingsTabNetwork": "Verkko", + "SettingsTabNetworkConnection": "Verkkoyhteys", + "SettingsTabCpuCache": "CPU välimuisti", + "SettingsTabCpuMemory": "CPU tila", + "DialogUpdaterFlatpakNotSupportedMessage": "Päivitä Ryujinx FlatHubin kautta.", + "UpdaterDisabledWarningTitle": "Päivittäjä on pois päältä!", + "ControllerSettingsRotate90": "Käännä 90° myötäpäivään", + "IconSize": "Kuvakkeen koko", + "IconSizeTooltip": "Muuta pelikuvakkeiden kokoa", + "MenuBarOptionsShowConsole": "Näytä konsoli", + "ShaderCachePurgeError": "Virhe puhdistaessa varjostin välimuistia kohdassa {0}: {1}", + "UserErrorNoKeys": "Avaimia ei löytynyt", + "UserErrorNoFirmware": "Laiteohjelmistoa ei löytynyt", + "UserErrorFirmwareParsingFailed": "Laiteohjelmiston jäsennysvirhe", + "UserErrorApplicationNotFound": "Sovellusta ei löydetty", + "UserErrorUnknown": "Tuntematon virhe", + "UserErrorUndefined": "Määrittelemätön virhe", + "UserErrorNoKeysDescription": "Ryujinx ei löytänyt 'prod.keys' tiedostoa", + "UserErrorNoFirmwareDescription": "Ryujinx ei löytäny yhtään asennettua laiteohjelmistoa", + "UserErrorFirmwareParsingFailedDescription": "Ryujinx ei pystynyt jäsentämään annettua laiteohjelmistoa. Tämä usein johtuu vanhentuneista avaimista.", + "UserErrorApplicationNotFoundDescription": "Ryujinx ei löytänyt kelvollista sovellusta annetulta polulta.", + "UserErrorUnknownDescription": "Tapahtui tuntematon virhe!", + "UserErrorUndefinedDescription": "Määrittelemätön virhe! Tämän ei pitäisi tapahtua, ota yhteyttä ohjelmistokehittäjään!", + "OpenSetupGuideMessage": "Avaa asennusopas", + "NoUpdate": "Ei päivitystä", + "TitleUpdateVersionLabel": "Versio {0}", + "TitleBundledUpdateVersionLabel": "Paketoitu: Versio {0}", + "TitleBundledDlcLabel": "Paketoitu:", + "RyujinxInfo": "Ryujinx - Informaatio", + "RyujinxConfirm": "Ryujinx - Varmistus", + "FileDialogAllTypes": "Kaikki tyypit", + "Never": "Ei koskaan", + "SwkbdMinCharacters": "Täytyy olla vähintään {0} merkkiä pitkä", + "SwkbdMinRangeCharacters": "Täytyy olla {0}-{1} merkkiä pitkä", + "SoftwareKeyboard": "Ohjelmistonäppäimistö", + "SoftwareKeyboardModeNumeric": "Täytyy olla vain 0-9 tai '.'", + "SoftwareKeyboardModeAlphabet": "CJK-merkkejä ei saa olla", + "SoftwareKeyboardModeASCII": "Täytyy olla vain ASCII-tekstiä", + "ControllerAppletControllers": "Tuetut ohjaimet:", + "ControllerAppletPlayers": "Pelaajat:", + "ControllerAppletDescription": "Nykyinen konfiguraatiosi on virheellinen. Avaa asetukset ja tee ohjain konfiguraatiosi uudelleen.", + "ControllerAppletDocked": "Telakoitu tila asetettu. Kannettava ohjaus pitäisi olla pois päältä.", + "UpdaterRenaming": "Uudelleennimetään vanhoja tiedostoja...", + "UpdaterRenameFailed": "Päivittäjä ei pystynyt uudelleennimetä tiedostoa: {0}", + "UpdaterAddingFiles": "Lisätään uusia tiedostoja...", + "UpdaterExtracting": "Puretaan päivitystä...", + "UpdaterDownloading": "Ladataan päivitys...", + "Game": "Peli", + "Docked": "Telakoitu", + "Handheld": "Kannettava", + "ConnectionError": "Yhteysvirhe.", + "AboutPageDeveloperListMore": "{0} ja lisää...", + "ApiError": "API-virhe.", + "LoadingHeading": "Ladataan {0}", + "CompilingPPTC": "Käännetään PTC:tä", + "CompilingShaders": "Käännetään varjostimia", + "AllKeyboards": "Kaikki näppäimistöt", + "OpenFileDialogTitle": "Valitse tuettu tiedosto avattavaksi", + "OpenFolderDialogTitle": "Valitse pakkaamattoman pelin kansio", + "AllSupportedFormats": "Kaikki tuetut muodot", + "RyujinxUpdater": "Ryujinx päivittäjä", + "SettingsTabHotkeys": "Näppäimistön pikanäppäimet", + "SettingsTabHotkeysHotkeys": "Näppäimistön pikanäppäimet", + "SettingsTabHotkeysToggleVsyncHotkey": "Vaihda VSync tilaa:", + "SettingsTabHotkeysScreenshotHotkey": "Kuvankaappaus:", + "SettingsTabHotkeysShowUiHotkey": "Näytä UI:", + "SettingsTabHotkeysPauseHotkey": "Pysäytä:", + "SettingsTabHotkeysToggleMuteHotkey": "Vaimenna:", + "ControllerMotionTitle": "Liikkeen asetukset", + "ControllerRumbleTitle": "Tärinä asetukset", + "SettingsSelectThemeFileDialogTitle": "Valitse teematiedosto", + "SettingsXamlThemeFile": "Xaml teematiedosto", + "AvatarWindowTitle": "Hallitse tilejä - Profiilikuva", + "Amiibo": "Amiibo", + "Unknown": "Tuntematon", + "Usage": "Käyttö", + "Writable": "Kirjoitettava", + "SelectDlcDialogTitle": "Valitse DLC-tiedostot", + "SelectUpdateDialogTitle": "Valitse päivitystiedostot", + "SelectModDialogTitle": "Valite modi hakemisto", + "UserProfileWindowTitle": "Käyttäjäprofiilien hallinta", + "CheatWindowTitle": "Huijauskoodien hallinta", + "DlcWindowTitle": "Hallitse ladattavaa sisältöä pelille {0} ({1})", + "ModWindowTitle": "Hallitse modeja pelille {0} ({1})", + "UpdateWindowTitle": "Otsikon päivitysten hallinta", + "CheatWindowHeading": "Huijauskoodit käytettävissä pelille {0} ({1})", + "BuildId": "Koontitunnus:", + "DlcWindowHeading": "{0} Ladattavaa sisältö(ä)", + "ModWindowHeading": "{0} Mod(ia)", + "UserProfilesEditProfile": "Muokkaa valittua", + "Cancel": "Peruuta", + "Save": "Tallenna", + "Discard": "Hylkää", + "Paused": "Keskeytetty", + "UserProfilesSetProfileImage": "Aseta profiilikuva", + "UserProfileEmptyNameError": "Nimi vaaditaan", + "UserProfileNoImageError": "Profiilikuva on asetettava", + "GameUpdateWindowHeading": "Hallitse päivityksiä pelille {0} ({1})", + "SettingsTabHotkeysResScaleUpHotkey": "Lisää resoluutiota:", + "SettingsTabHotkeysResScaleDownHotkey": "Vähennä resoluutiota:", + "UserProfilesName": "Nimi:", + "UserProfilesUserId": "Käyttäjän ID:", + "SettingsTabGraphicsBackend": "Grafiikka taustajärjestelmä", + "SettingsTabGraphicsBackendTooltip": "Valitse emulaattorissa käytettävä grafiikkataustajärjestelmä.\n\nVulkan on yleisesti parempi kaikille nykyaikaisille näytönohjaimille, kunhan niiden ajurit ovat ajan tasalla. Vulkan sisältää myös nopeamman varjostimen kääntämisen (vähemmän pätkimistä) kaikilla näytönohjain merkeillä.\n\nOpenGL voi saavuttaa parempia tuloksia vanhemmilla Nvidia näytönohjaimilla, vanhoilla AMD näytönohjaimilla Linuxilla, tai näytönohjaimilla, joissa on vähemmän VRAMia, mutta varjostin kääntäminen tulee pätkimään enemmän.\n\nAseta Vulkanille, jos olet epävarma. Aseta OpenGL:lle jos sinun näytönohjain ei tue Vulkania edes uusimmilla grafiikka ajureilla.", + "SettingsEnableTextureRecompression": "Ota käyttöön tekstuurejen uudelleenpakkaus", + "SettingsEnableTextureRecompressionTooltip": "Pakkaa ASTC-tekstuurit VRAM-muisitn käytön vähentämiseksi.\n\nPelit, jotka käyttävät tätä tekstuurimuotoa ovat Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder ja The Legend of Zelda: Tears of the Kingdom.\n\nNäytönohjaimet, joissa on 4 GB VRAMia tai vähemmän tulevät todennäköisesti kaatumaan jossain vaiheessa näitä pelejä pelatessa.\n\nOta käyttöön vain jos sinulta loppuu VRAM kesken näissä peleissä. Jätä pois päältä, jos olet epävarma.", + "SettingsTabGraphicsPreferredGpu": "Ensisijainen näytönohjain", + "SettingsTabGraphicsPreferredGpuTooltip": "Valitse näytönohjain, jota käytetään Vulkan grafiikka taustajärjestelmän kanssa.\n\nEi vaikuta OpenGL:n käyttämään näytönohjaimeen.\n\nAseta näytönohjain merkittynä \"dGPU\", jos olet epävarma. Jos tätä ei ole, jätä koskematta.", + "SettingsAppRequiredRestartMessage": "Ryujinxin uudelleenkäynnistys vaaditaan", + "SettingsGpuBackendRestartMessage": "Grafiikkataustajärjestelmä tai näytönohjain asetuksia on muutettu. Tämä vaatii uudelleenkäynnistyksen, jotta ne voidaan ottaa käyttöön.", + "SettingsGpuBackendRestartSubMessage": "Haluatko käynnistää uudelleen nyt?", + "RyujinxUpdaterMessage": "Haluatko päivittää Ryujinxin uusimpaan versioon?", + "SettingsTabHotkeysVolumeUpHotkey": "Lisää äänenvoimakkuutta:", + "SettingsTabHotkeysVolumeDownHotkey": "Vähennä äänenvoimakkuutta:", + "SettingsEnableMacroHLE": "Ota Macro HLE käyttöön", + "SettingsEnableMacroHLETooltip": "Korkean tason GPU Macro koodin emulointi.\n\nParantaa suorityskykyä, mutta voi aiheuttaa graafisia ongelmia joissain peleissä.\n\nJätä päälle, jos olet epävarma.", + "SettingsEnableColorSpacePassthrough": "Color Space Passthrough", + "SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.", + "VolumeShort": "Ääni", + "UserProfilesManageSaves": "Hallitse tallennuksia", + "DeleteUserSave": "Haluatko poistaa käyttäjän tallennuksen tästä pelistä?", + "IrreversibleActionNote": "Tätä toimintoa ei voi peruuttaa.", + "SaveManagerHeading": "Hallitse {0}:n tallennuksia ({1})", + "SaveManagerTitle": "Tallennusten hallinta", + "Name": "Nimi", + "Size": "Koko", + "Search": "Hae", + "UserProfilesRecoverLostAccounts": "Palauta menetetty tili", + "Recover": "Palauta", + "UserProfilesRecoverHeading": "Tallennuksia löydettiin seuraaville tileille", + "UserProfilesRecoverEmptyList": "Ei palautettavia profiileja", + "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", + "GraphicsAALabel": "Anti-Aliasing:", + "GraphicsScalingFilterLabel": "Scaling Filter:", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterBilinear": "Bilinear", + "GraphicsScalingFilterNearest": "Nearest", + "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Alue", + "GraphicsScalingFilterLevelLabel": "Taso", + "GraphicsScalingFilterLevelTooltip": "Aseta FSR 1.0 terävöitystaso. Korkeampi on terävempi.", + "SmaaLow": "SMAA Low", + "SmaaMedium": "SMAA Medium", + "SmaaHigh": "SMAA High", + "SmaaUltra": "SMAA Ultra", + "UserEditorTitle": "Muokkaa käyttäjää", + "UserEditorTitleCreate": "Luo käyttäjä", + "SettingsTabNetworkInterface": "Network Interface:", + "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", + "NetworkInterfaceDefault": "Oletus", + "PackagingShaders": "Pakataan varjostimia", + "AboutChangelogButton": "Näytä muutosloki GitHubissa", + "AboutChangelogButtonTooltipMessage": "Klikkaa avataksesi tämän version muutosloki oletusselaimessasi.", + "SettingsTabNetworkMultiplayer": "Moninpeli", + "MultiplayerMode": "Tila:", + "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", + "MultiplayerModeDisabled": "Pois käytöstä", + "MultiplayerModeLdnMitm": "ldn_mitm" +} diff --git a/src/Ryujinx/Assets/Locales/fr_FR.json b/src/Ryujinx/Assets/Locales/fr_FR.json index 99a060650..c4fc8a0b1 100644 --- a/src/Ryujinx/Assets/Locales/fr_FR.json +++ b/src/Ryujinx/Assets/Locales/fr_FR.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Utiliser l'Hyperviseur", "MenuBarFile": "_Fichier", "MenuBarFileOpenFromFile": "_Charger un jeu depuis un fichier", + "MenuBarFileOpenFromFileError": "Aucune application trouvée dans le fichier sélectionné.", "MenuBarFileOpenUnpacked": "Charger un jeu extrait", "MenuBarFileOpenEmuFolder": "Ouvrir le dossier Ryujinx", "MenuBarFileOpenLogsFolder": "Ouvrir le dossier des journaux", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Gérer les types de fichiers", "MenuBarToolsInstallFileTypes": "Installer les types de fichiers", "MenuBarToolsUninstallFileTypes": "Désinstaller les types de fichiers", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Affichage", + "MenuBarViewWindow": "Taille de la Fenêtre", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Aide", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Activer Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage", "SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Se souvenir de la taille/position de la fenêtre", "SettingsTabGeneralHideCursor": "Masquer le Curseur :", "SettingsTabGeneralHideCursorNever": "Jamais", "SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": "Cela peut causer des instabilités", - "SettingsTabSystemExpandDramSize": "Utiliser disposition alternative de la mémoire (développeur)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorer les services manquants", "SettingsTabGraphics": "Graphismes", "SettingsTabGraphicsAPI": "API Graphique", @@ -271,59 +272,59 @@ "ControllerSettingsMotionGyroDeadzone": "Zone morte du gyroscope:", "ControllerSettingsSave": "Enregistrer", "ControllerSettingsClose": "Fermer", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", + "KeyUnknown": "Inconnu", + "KeyShiftLeft": "Shift Gauche", + "KeyShiftRight": "Shift Droit", + "KeyControlLeft": "Ctrl Gauche", + "KeyMacControlLeft": "⌃ Gauche", + "KeyControlRight": "Ctrl Droite", + "KeyMacControlRight": "⌃ Droite", + "KeyAltLeft": "Alt Gauche", + "KeyMacAltLeft": "⌥ Gauche", + "KeyAltRight": "Alt Droite", + "KeyMacAltRight": "⌥ Droite", + "KeyWinLeft": "⊞ Gauche", + "KeyMacWinLeft": "⌘ Gauche", + "KeyWinRight": "⊞ Droite", + "KeyMacWinRight": "⌘ Droite", "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", + "KeyUp": "Haut", + "KeyDown": "Bas", + "KeyLeft": "Gauche", + "KeyRight": "Droite", + "KeyEnter": "Entrée", + "KeyEscape": "Échap", + "KeySpace": "Espace", + "KeyTab": "Tabulation", + "KeyBackSpace": "Effacer", + "KeyInsert": "Insérer", + "KeyDelete": "Supprimer", + "KeyPageUp": "Page Haut", + "KeyPageDown": "Page Bas", + "KeyHome": "Accueil", + "KeyEnd": "Fin", + "KeyCapsLock": "Verr. Maj.", + "KeyScrollLock": "Arr. Déf.", + "KeyPrintScreen": "Impr. Écr.", "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", + "KeyNumLock": "Verr. Num.", + "KeyClear": "Vider", + "KeyKeypad0": "Pavé Numérique 0", + "KeyKeypad1": "Pavé Numérique 1", + "KeyKeypad2": "Pavé Numérique 2", + "KeyKeypad3": "Pavé Numérique 3", + "KeyKeypad4": "Pavé Numérique 4", + "KeyKeypad5": "Pavé Numérique 5", + "KeyKeypad6": "Pavé Numérique 6", + "KeyKeypad7": "Pavé Numérique 7", + "KeyKeypad8": "Pavé Numérique 8", + "KeyKeypad9": "Pavé Numérique 9", + "KeyKeypadDivide": "Pavé Numérique Diviser", + "KeyKeypadMultiply": "Pavé Numérique Multiplier", + "KeyKeypadSubtract": "Pavé Numérique Soustraction", + "KeyKeypadAdd": "Pavé Numérique Additionner", + "KeyKeypadDecimal": "Pavé Numérique Décimal", + "KeyKeypadEnter": "Pavé Numérique Entrée", "KeyNumber0": "0", "KeyNumber1": "1", "KeyNumber2": "2", @@ -346,32 +347,32 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "KeyUnbound": "Non lié", + "GamepadLeftStick": "Bouton Joystick Gauche", + "GamepadRightStick": "Bouton Joystick Droit", + "GamepadLeftShoulder": "Gâchette Haute Gauche", + "GamepadRightShoulder": "Gâchette Haute Droite", + "GamepadLeftTrigger": "Gâchette Gauche", + "GamepadRightTrigger": "Gâchette Droite", + "GamepadDpadUp": "Haut", + "GamepadDpadDown": "Bas", + "GamepadDpadLeft": "Gauche", + "GamepadDpadRight": "Droite", "GamepadMinus": "-", "GamepadPlus": "+", "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", + "GamepadMisc1": "Divers", "GamepadPaddle1": "Paddle 1", "GamepadPaddle2": "Paddle 2", "GamepadPaddle3": "Paddle 3", "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "GamepadTouchpad": "Pavé Tactile", + "GamepadSingleLeftTrigger0": "Gâchette Gauche 0", + "GamepadSingleRightTrigger0": "Gâchette Droite 0", + "GamepadSingleLeftTrigger1": "Gâchette Gauche 1", + "GamepadSingleRightTrigger1": "Gâchette Droite 1", + "StickLeft": "Joystick Gauche", + "StickRight": "Joystick Droit", "UserProfilesSelectedUserProfile": "Profil utilisateur sélectionné :", "UserProfilesSaveProfileName": "Enregistrer le nom du profil", "UserProfilesChangeProfileImage": "Changer l'image du profil", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Ajouter/Retirer des favoris", "GameListContextMenuToggleFavoriteToolTip": "Activer/désactiver le statut favori du jeu", "SettingsTabGeneralTheme": "Thème :", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "Sombre", "SettingsTabGeneralThemeLight": "Clair", "ControllerSettingsConfigureGeneral": "Configurer", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "Mappez directement la mémoire dans l'espace d'adresses de l'hôte. Compilation et exécution JIT beaucoup plus rapides.", "MemoryManagerUnsafeTooltip": "Mapper directement la mémoire dans la carte, mais ne pas masquer l'adresse dans l'espace d'adressage du client avant l'accès. Plus rapide, mais la sécurité sera négliger. L'application peut accéder à la mémoire depuis n'importe où dans Ryujinx, donc exécutez uniquement les programmes en qui vous avez confiance avec ce mode.", "UseHypervisorTooltip": "Utiliser l'Hyperviseur au lieu du JIT. Améliore considérablement les performances lorsqu'il est disponible, mais peut être instable dans son état actuel.", - "DRamTooltip": "Utilise une disposition alternative de la mémoire pour imiter le kit de développeur de la Switch.\n\nActiver cette option uniquement pour les packs de textures 4k ou les mods à résolution 4k.\nN'améliore pas les performances, cause des crashs dans certains jeux si activer.\n\nLaissez Désactiver en cas d'incertitude.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignore les services Horizon OS non-intégré. Cela peut aider à contourner les plantages lors du démarrage de certains jeux.\n\nActivez-le en cas d'incertitude.", "GraphicsBackendThreadingTooltip": "Exécute des commandes du backend graphiques sur un second thread.\n\nAccélère la compilation des shaders, réduit les crashs et les lags, améliore les performances sur les pilotes GPU sans support de multithreading. Légère augementation des performances sur les pilotes avec multithreading intégrer.\n\nRéglez sur Auto en cas d'incertitude.", "GalThreadingTooltip": "Exécute des commandes du backend graphiques sur un second thread.\n\nAccélère la compilation des shaders, réduit les crashs et les lags, améliore les performances sur les pilotes GPU sans support de multithreading. Légère augementation des performances sur les pilotes avec multithreading intégrer.\n\nRéglez sur Auto en cas d'incertitude.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Ouvrir le guide d'installation", "NoUpdate": "Aucune mise à jour", "TitleUpdateVersionLabel": "Version {0}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Confirmation", "FileDialogAllTypes": "Tous les types", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "FXAA floute la plupart de l'image, tandis que SMAA tente de détecter les contours dentelés et de les lisser.\n\nIl n'est pas recommandé de l'utiliser en conjonction avec le filtre de mise à l'échelle FSR.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'apparence souhaitée pour un jeu.\n\nLaissez sur NONE si vous n'êtes pas sûr.", "GraphicsAALabel": "Anticrénelage :", "GraphicsScalingFilterLabel": "Filtre de mise à l'échelle :", - "GraphicsScalingFilterTooltip": "Choisissez le filtre de mise à l'échelle qui sera appliqué lors de l'utilisation de la mise à l'échelle de la résolution.\n\nLe filtre bilinéaire fonctionne bien pour les jeux en 3D et constitue une option par défaut sûre.\n\nLe filtre le plus proche est recommandé pour les jeux de pixel art.\n\nFSR 1.0 est simplement un filtre de netteté, non recommandé pour une utilisation avec FXAA ou SMAA.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'aspect souhaité pour un jeu.\n\nLaissez sur BILINEAR si vous n'êtes pas sûr.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinéaire", "GraphicsScalingFilterNearest": "Le plus proche", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Niveau ", "GraphicsScalingFilterLevelTooltip": "Définissez le niveau de netteté FSR 1.0. Plus élevé signifie plus net.", "SmaaLow": "SMAA Faible", diff --git a/src/Ryujinx/Assets/Locales/he_IL.json b/src/Ryujinx/Assets/Locales/he_IL.json index 848f78080..ef122a9a1 100644 --- a/src/Ryujinx/Assets/Locales/he_IL.json +++ b/src/Ryujinx/Assets/Locales/he_IL.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "השתמש ב Hypervisor", "MenuBarFile": "_קובץ", "MenuBarFileOpenFromFile": "_טען יישום מקובץ", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "טען משחק _שאינו ארוז", "MenuBarFileOpenEmuFolder": "פתח את תיקיית ריוג'ינקס", "MenuBarFileOpenLogsFolder": "פתח את תיקיית קבצי הלוג", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "ניהול סוגי קבצים", "MenuBarToolsInstallFileTypes": "סוגי קבצי התקנה", "MenuBarToolsUninstallFileTypes": "סוגי קבצי הסרה", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_תצוגה", + "MenuBarViewWindow": "גודל חלון", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_עזרה", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "הפעלת תצוגה עשירה בדיסקורד", "SettingsTabGeneralCheckUpdatesOnLaunch": "בדוק אם קיימים עדכונים בהפעלה", "SettingsTabGeneralShowConfirmExitDialog": "הראה דיאלוג \"אשר יציאה\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "זכור גודל ומיקום חלון", "SettingsTabGeneralHideCursor": "הסתר את הסמן", "SettingsTabGeneralHideCursorNever": "אף פעם", "SettingsTabGeneralHideCursorOnIdle": "במצב סרק", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "האצות", "SettingsTabSystemHacksNote": "עלול לגרום לאי יציבות", - "SettingsTabSystemExpandDramSize": "השתמש בפריסת זיכרון חלופית (נועד למפתחים)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "התעלם משירותים חסרים", "SettingsTabGraphics": "גרפיקה", "SettingsTabGraphicsAPI": "ממשק גראפי", @@ -271,7 +272,7 @@ "ControllerSettingsMotionGyroDeadzone": "שטח מת של הג'ירוסקופ:", "ControllerSettingsSave": "שמירה", "ControllerSettingsClose": "סגירה", - "KeyUnknown": "Unknown", + "KeyUnknown": "לא ידוע", "KeyShiftLeft": "Shift Left", "KeyShiftRight": "Shift Right", "KeyControlLeft": "Ctrl Left", @@ -286,23 +287,23 @@ "KeyMacWinLeft": "⌘ Left", "KeyWinRight": "⊞ Right", "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyMenu": "תַפרִיט", + "KeyUp": "למעלה", + "KeyDown": "למטה", + "KeyLeft": "שמאלה", + "KeyRight": "ימינה", "KeyEnter": "Enter", "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", + "KeySpace": "רווח", + "KeyTab": "טאב", "KeyBackSpace": "Backspace", "KeyInsert": "Insert", "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", + "KeyPageUp": "דיפדוף מעלה", + "KeyPageDown": "דיפדוף כלפי מטה", "KeyHome": "Home", "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", + "KeyCapsLock": "נעילה אותיות גדולות", "KeyScrollLock": "Scroll Lock", "KeyPrintScreen": "Print Screen", "KeyPause": "Pause", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "למתג העדפה", "GameListContextMenuToggleFavoriteToolTip": "למתג סטטוס העדפה של משחק", "SettingsTabGeneralTheme": "ערכת נושא:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "כהה", "SettingsTabGeneralThemeLight": "בהיר", "ControllerSettingsConfigureGeneral": "הגדר", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "ממפה זיכרון ישירות לכתובת המארח. מהיר בהרבה ביכולות קימפול ה-JIT והריצה.", "MemoryManagerUnsafeTooltip": "ממפה זיכרון ישירות, אך לא ממסך את הכתובת בתוך כתובת המארח לפני הגישה. מהיר, אך במחיר של הגנה. יישום המארח בעל גישה לזיכרון מכל מקום בריוג'ינקס, לכן הריצו איתו רק קבצים שאתם סומכים עליהם.", "UseHypervisorTooltip": "השתמש ב- Hypervisor במקום JIT. משפר מאוד ביצועים כשניתן, אבל יכול להיות לא יציב במצבו הנוכחי.", - "DRamTooltip": "מנצל תצורת מצב-זיכרון חלופית לחכות את מכשיר הפיתוח של הסוויץ'.\n\nזה שימושי להחלפת חבילות מרקמים באיכותיים יותר או כאלו ברזולוציית 4k. לא משפר ביצועים.\n\nמוטב להשאיר כבוי אם לא בטוחים.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "מתעלם מפעולות שלא קיבלו מימוש במערכת ההפעלה Horizon OS. זה עלול לעזור לעקוף קריסות של היישום במשחקים מסויימים.\n\nמוטב להשאיר כבוי אם לא בטוחים.", "GraphicsBackendThreadingTooltip": "מריץ פקודות גראפיקה בתהליך שני נפרד.\n\nמאיץ עיבוד הצללות, מפחית תקיעות ומשפר ביצועים של דרייבר כרטיסי מסך אשר לא תומכים בהרצה רב-תהליכית.\n\nמוטב להשאיר על אוטומטי אם לא בטוחים.", "GalThreadingTooltip": "מריץ פקודות גראפיקה בתהליך שני נפרד.\n\nמאיץ עיבוד הצללות, מפחית תקיעות ומשפר ביצועים של דרייבר כרטיסי מסך אשר לא תומכים בהרצה רב-תהליכית.\n\nמוטב להשאיר על אוטומטי אם לא בטוחים.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "פתח מדריך התקנה", "NoUpdate": "אין עדכון", "TitleUpdateVersionLabel": "גרסה {0}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "ריוג'ינקס - מידע", "RyujinxConfirm": "ריוג'ינקס - אישור", "FileDialogAllTypes": "כל הסוגים", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAALabel": "החלקת-עקומות:", "GraphicsScalingFilterLabel": "מסנן מידת איכות:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "רמה", "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", "SmaaLow": "SMAA נמוך", diff --git a/src/Ryujinx/Assets/Locales/it_IT.json b/src/Ryujinx/Assets/Locales/it_IT.json index 280ebd880..ef8d5a9bd 100644 --- a/src/Ryujinx/Assets/Locales/it_IT.json +++ b/src/Ryujinx/Assets/Locales/it_IT.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Usa Hypervisor", "MenuBarFile": "_File", "MenuBarFileOpenFromFile": "_Carica applicazione da un file", + "MenuBarFileOpenFromFileError": "Applicazione non trovata nel file selezionato", "MenuBarFileOpenUnpacked": "Carica _gioco estratto", "MenuBarFileOpenEmuFolder": "Apri cartella di Ryujinx", "MenuBarFileOpenLogsFolder": "Apri cartella dei log", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Gestisci i tipi di file", "MenuBarToolsInstallFileTypes": "Installa i tipi di file", "MenuBarToolsUninstallFileTypes": "Disinstalla i tipi di file", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Visualizza", + "MenuBarViewWindow": "Dimensione finestra", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Aiuto", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Attiva Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Controlla aggiornamenti all'avvio", "SettingsTabGeneralShowConfirmExitDialog": "Mostra dialogo \"Conferma Uscita\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Ricorda la dimensione e la posizione della finestra", "SettingsTabGeneralHideCursor": "Nascondi il cursore:", "SettingsTabGeneralHideCursorNever": "Mai", "SettingsTabGeneralHideCursorOnIdle": "Quando è inattivo", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Espedienti", "SettingsTabSystemHacksNote": "Possono causare instabilità", - "SettingsTabSystemExpandDramSize": "Usa layout di memoria alternativo (per sviluppatori)", + "SettingsTabSystemExpandDramSize": "Espandi DRAM a 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignora servizi mancanti", "SettingsTabGraphics": "Grafica", "SettingsTabGraphicsAPI": "API grafica", @@ -307,7 +308,7 @@ "KeyPrintScreen": "Stamp", "KeyPause": "Pausa", "KeyNumLock": "Bloc Num", - "KeyClear": "Clear", + "KeyClear": "Elimina", "KeyKeypad0": "Tast. num. 0", "KeyKeypad1": "Tast. num. 1", "KeyKeypad2": "Tast. num. 2", @@ -359,12 +360,12 @@ "GamepadDpadRight": "Destra", "GamepadMinus": "-", "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", + "GamepadGuide": "Guida", + "GamepadMisc1": "Altro", + "GamepadPaddle1": "Tasto extra 1", + "GamepadPaddle2": "Tasto extra 2", + "GamepadPaddle3": "Tasto extra 3", + "GamepadPaddle4": "Tasto extra 4", "GamepadTouchpad": "Touchpad", "GamepadSingleLeftTrigger0": "Grilletto sinistro 0", "GamepadSingleRightTrigger0": "Grilletto destro 0", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Preferito", "GameListContextMenuToggleFavoriteToolTip": "Segna il gioco come preferito", "SettingsTabGeneralTheme": "Tema:", + "SettingsTabGeneralThemeAuto": "Automatico", "SettingsTabGeneralThemeDark": "Scuro", "SettingsTabGeneralThemeLight": "Chiaro", "ControllerSettingsConfigureGeneral": "Configura", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "Mappa direttamente la memoria nello spazio degli indirizzi dell'host. Compilazione ed esecuzione JIT molto più veloce.", "MemoryManagerUnsafeTooltip": "Mappa direttamente la memoria, ma non maschera l'indirizzo all'interno dello spazio degli indirizzi guest prima dell'accesso. Più veloce, ma a costo della sicurezza. L'applicazione guest può accedere alla memoria da qualsiasi punto di Ryujinx, quindi esegui solo programmi di cui ti fidi con questa modalità.", "UseHypervisorTooltip": "Usa Hypervisor invece di JIT. Migliora notevolmente le prestazioni quando disponibile, ma può essere instabile nel suo stato attuale.", - "DRamTooltip": "Utilizza un layout di memoria alternativo per imitare un'unità di sviluppo di Switch.\n\nQuesta opzione è utile soltanto per i pacchetti di texture ad alta risoluzione o per le mod che aumentano la risoluzione a 4K. NON migliora le prestazioni.\n\nNel dubbio, lascia l'opzione disattivata.", + "DRamTooltip": "Utilizza una modalità di memoria alternativa con 8GiB di DRAM per imitare un'unità di sviluppo di Switch.\n\nÈ utile solo per i pacchetti di texture ad alta risoluzione o per le mod in 4K. NON migliora le prestazioni.\n\nNel dubbio, lascia l'opzione disattivata.", "IgnoreMissingServicesTooltip": "Ignora i servizi non implementati del sistema operativo Horizon. Può aiutare ad aggirare gli arresti anomali che si verificano avviando alcuni giochi.\n\nNel dubbio, lascia l'opzione disattivata.", "GraphicsBackendThreadingTooltip": "Esegue i comandi del backend grafico su un secondo thread.\n\nVelocizza la compilazione degli shader, riduce lo stuttering e migliora le prestazioni sui driver grafici senza il supporto integrato al multithreading. Migliora leggermente le prestazioni sui driver che supportano il multithreading.\n\nNel dubbio, imposta l'opzione su Auto.", "GalThreadingTooltip": "Esegue i comandi del backend grafico su un secondo thread.\n\nVelocizza la compilazione degli shader, riduce lo stuttering e migliora le prestazioni sui driver grafici senza il supporto integrato al multithreading. Migliora leggermente le prestazioni sui driver che supportano il multithreading.\n\nNel dubbio, imposta l'opzione su Auto.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Apri la guida all'installazione", "NoUpdate": "Nessun aggiornamento", "TitleUpdateVersionLabel": "Versione {0}", + "TitleBundledUpdateVersionLabel": "Incluso: Versione {0}", + "TitleBundledDlcLabel": "Incluso:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Conferma", "FileDialogAllTypes": "Tutti i tipi", @@ -727,9 +731,9 @@ "SettingsEnableTextureRecompression": "Attiva la ricompressione delle texture", "SettingsEnableTextureRecompressionTooltip": "Comprime le texture ASTC per ridurre l'utilizzo di VRAM.\n\nI giochi che utilizzano questo formato di texture includono Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder e The Legend of Zelda: Tears of the Kingdom.\n\nLe schede grafiche con 4GiB o meno di VRAM probabilmente si bloccheranno ad un certo punto durante l'esecuzione di questi giochi.\n\nAttiva questa opzione solo se sei a corto di VRAM nei giochi sopra menzionati. Nel dubbio, lascia l'opzione disattivata.", "SettingsTabGraphicsPreferredGpu": "GPU preferita", - "SettingsTabGraphicsPreferredGpuTooltip": "Seleziona la scheda grafica che verrà usata con la backend grafica Vulkan.\n\nNon influenza la GPU che userà OpenGL.\n\nImposta la GPU contrassegnata come \"dGPU\" se non sei sicuro. Se non ce n'è una, lascia intatta quest'impostazione.", + "SettingsTabGraphicsPreferredGpuTooltip": "Seleziona la scheda grafica che verrà usata con il backend grafico Vulkan.\n\nL'opzione non modifica la GPU usata da OpenGL.\n\nNel dubbio, seleziona la GPU contrassegnata come \"dGPU\". Se non ce n'è una, lascia intatta questa opzione.", "SettingsAppRequiredRestartMessage": "È richiesto un riavvio di Ryujinx", - "SettingsGpuBackendRestartMessage": "Le impostazioni della backend grafica o della GPU sono state modificate. Questo richiederà un riavvio perché le modifiche siano applicate", + "SettingsGpuBackendRestartMessage": "Le impostazioni del backend grafico o della GPU sono state modificate. È necessario un riavvio per applicare le modifiche", "SettingsGpuBackendRestartSubMessage": "Vuoi riavviare ora?", "RyujinxUpdaterMessage": "Vuoi aggiornare Ryujinx all'ultima versione?", "SettingsTabHotkeysVolumeUpHotkey": "Alza il volume:", @@ -751,13 +755,14 @@ "Recover": "Recupera", "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account", "UserProfilesRecoverEmptyList": "Nessun profilo da recuperare", - "GraphicsAATooltip": "Applica anti-aliasing al rendering del gioco.\n\nFXAA sfocerà la maggior parte dell'immagine, mentre SMAA tenterà di trovare bordi frastagliati e lisciarli.\n\nNon si consiglia di usarlo in combinazione con il filtro di scala FSR.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nLasciare su Nessuno se incerto.", + "GraphicsAATooltip": "Applica l'anti-aliasing al rendering del gioco.\n\nFXAA rende la maggior parte dell'immagine sfocata, mentre SMAA tenta di rilevare e smussare i bordi frastagliati.\n\nSi consiglia di non usarlo in combinazione con il filtro di scaling FSR.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nNel dubbio, lascia su Nessuno.", "GraphicsAALabel": "Anti-Aliasing:", "GraphicsScalingFilterLabel": "Filtro di scala:", - "GraphicsScalingFilterTooltip": "Scegli il filtro di scaling che verrà applicato quando si utilizza o scaling di risoluzione.\n\nBilineare funziona bene per i giochi 3D ed è un'opzione predefinita affidabile.\n\nNearest è consigliato per i giochi in pixel art.\n\nFSR 1.0 è solo un filtro di nitidezza, non raccomandato per l'uso con FXAA o SMAA.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nLasciare su Bilineare se incerto.", + "GraphicsScalingFilterTooltip": "Scegli il filtro di scaling che verrà applicato quando si utilizza lo scaling della risoluzione.\n\nBilineare funziona bene per i giochi 3D ed è un'opzione predefinita affidabile.\n\nNearest è consigliato per i giochi in pixel art.\n\nFSR 1.0 è solo un filtro di nitidezza, sconsigliato per l'uso con FXAA o SMAA.\n\nLo scaling ad area è consigliato quando si riducono delle risoluzioni che sono più grandi della finestra di output. Può essere usato per ottenere un effetto di anti-aliasing supercampionato quando si riduce di più di 2x.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nNel dubbio, lascia su BILINEARE.", "GraphicsScalingFilterBilinear": "Bilineare", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Livello", "GraphicsScalingFilterLevelTooltip": "Imposta il livello di nitidezza di FSR 1.0. Valori più alti comportano una maggiore nitidezza.", "SmaaLow": "SMAA Basso", diff --git a/src/Ryujinx/Assets/Locales/ja_JP.json b/src/Ryujinx/Assets/Locales/ja_JP.json index 61e963258..4d91d3639 100644 --- a/src/Ryujinx/Assets/Locales/ja_JP.json +++ b/src/Ryujinx/Assets/Locales/ja_JP.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "ハイパーバイザーを使用", "MenuBarFile": "ファイル(_F)", "MenuBarFileOpenFromFile": "ファイルからアプリケーションをロード(_L)", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "展開されたゲームをロード", "MenuBarFileOpenEmuFolder": "Ryujinx フォルダを開く", "MenuBarFileOpenLogsFolder": "ログフォルダを開く", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "ファイル形式を管理", "MenuBarToolsInstallFileTypes": "ファイル形式をインストール", "MenuBarToolsUninstallFileTypes": "ファイル形式をアンインストール", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "表示(_V)", + "MenuBarViewWindow": "ウィンドウサイズ", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "ヘルプ(_H)", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Discord リッチプレゼンスを有効にする", "SettingsTabGeneralCheckUpdatesOnLaunch": "起動時にアップデートを確認する", "SettingsTabGeneralShowConfirmExitDialog": "\"終了を確認\" ダイアログを表示する", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "ウィンドウのサイズと位置を記憶する", "SettingsTabGeneralHideCursor": "マウスカーソルを非表示", "SettingsTabGeneralHideCursorNever": "決して", "SettingsTabGeneralHideCursorOnIdle": "アイドル時", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "ハック", "SettingsTabSystemHacksNote": " (挙動が不安定になる可能性があります)", - "SettingsTabSystemExpandDramSize": "DRAMサイズを6GiBに拡大する", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "未実装サービスを無視する", "SettingsTabGraphics": "グラフィックス", "SettingsTabGraphicsAPI": "グラフィックスAPI", @@ -346,7 +347,7 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", + "KeyUnbound": "未登録", "GamepadLeftStick": "L Stick Button", "GamepadRightStick": "R Stick Button", "GamepadLeftShoulder": "Left Shoulder", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "お気に入りを切り替え", "GameListContextMenuToggleFavoriteToolTip": "ゲームをお気に入りに含めるかどうかを切り替えます", "SettingsTabGeneralTheme": "テーマ:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "ダーク", "SettingsTabGeneralThemeLight": "ライト", "ControllerSettingsConfigureGeneral": "設定", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "ホストのアドレス空間にメモリを直接マップします.JITのコンパイルと実行速度が大きく向上します.", "MemoryManagerUnsafeTooltip": "メモリを直接マップしますが, アクセス前にゲストのアドレス空間内のアドレスをマスクしません. より高速になりますが, 安全性が犠牲になります. ゲストアプリケーションは Ryujinx のどこからでもメモリにアクセスできるので,このモードでは信頼できるプログラムだけを実行するようにしてください.", "UseHypervisorTooltip": "JIT の代わりにハイパーバイザーを使用します. 利用可能な場合, パフォーマンスが大幅に向上しますが, 現在の状態では不安定になる可能性があります.", - "DRamTooltip": "エミュレートされたシステムのメモリ容量を 4GiB から 6GiB に増加します.\n\n高解像度のテクスチャパックや 4K解像度の mod を使用する場合に有用です. パフォーマンスを改善するものではありません.\n\nよくわからない場合はオフのままにしてください.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "未実装の Horizon OS サービスを無視します. 特定のゲームにおいて起動時のクラッシュを回避できる場合があります.\n\nよくわからない場合はオフのままにしてください.", "GraphicsBackendThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", "GalThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", @@ -616,7 +618,7 @@ "GameListContextMenuManageCheatToolTip": "チートを管理します", "GameListContextMenuManageCheat": "チートを管理", "GameListContextMenuManageModToolTip": "Modを管理します", - "GameListContextMenuManageMod": "Manage Mods", + "GameListContextMenuManageMod": "Modを管理", "ControllerSettingsStickRange": "範囲:", "DialogStopEmulationTitle": "Ryujinx - エミュレーションを中止", "DialogStopEmulationMessage": "エミュレーションを中止してよろしいですか?", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "セットアップガイドを開く", "NoUpdate": "アップデートなし", "TitleUpdateVersionLabel": "バージョン {0} - {1}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - 情報", "RyujinxConfirm": "Ryujinx - 確認", "FileDialogAllTypes": "すべての種別", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "ゲームレンダリングにアンチエイリアスを適用します.\n\nFXAAは画像の大部分をぼかし, SMAAはギザギザのエッジを見つけて滑らかにします.\n\nFSRスケーリングフィルタとの併用は推奨しません.\n\nこのオプションは, ゲーム実行中に下の「適用」をクリックして変更できます. 設定ウィンドウを脇に移動し, ゲームが好みの表示になるように試してみてください.\n\n不明な場合は「なし」のままにしておいてください.", "GraphicsAALabel": "アンチエイリアス:", "GraphicsScalingFilterLabel": "スケーリングフィルタ:", - "GraphicsScalingFilterTooltip": "解像度変更時に適用されるスケーリングフィルタを選択します.\n\nBilinearは3Dゲームに適しており, 安全なデフォルトオプションです.\n\nピクセルアートゲームにはNearestを推奨します.\n\nFSR 1.0は単なるシャープニングフィルタであり, FXAAやSMAAとの併用は推奨されません.\n\nこのオプションは, ゲーム実行中に下の「適用」をクリックすることで変更できます. 設定ウィンドウを脇に移動し, ゲームが好みの表示になるように試してみてください.\n\n不明な場合はBilinearのままにしておいてください.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "レベル", "GraphicsScalingFilterLevelTooltip": "FSR 1.0のシャープ化レベルを設定します. 高い値ほどシャープになります.", "SmaaLow": "SMAA Low", diff --git a/src/Ryujinx/Assets/Locales/ko_KR.json b/src/Ryujinx/Assets/Locales/ko_KR.json index a92d084e0..8e23f7872 100644 --- a/src/Ryujinx/Assets/Locales/ko_KR.json +++ b/src/Ryujinx/Assets/Locales/ko_KR.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "하이퍼바이저 사용하기", "MenuBarFile": "_파일", "MenuBarFileOpenFromFile": "_파일에서 응용 프로그램 불러오기", + "MenuBarFileOpenFromFileError": "선택한 파일에서 애플리케이션을 찾을 수 없습니다.", "MenuBarFileOpenUnpacked": "_압축을 푼 게임 불러오기", "MenuBarFileOpenEmuFolder": "Ryujinx 폴더 열기", "MenuBarFileOpenLogsFolder": "로그 폴더 열기", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "해킹", "SettingsTabSystemHacksNote": "불안정성을 유발할 수 있음", - "SettingsTabSystemExpandDramSize": "대체 메모리 레이아웃 사용(개발자)", + "SettingsTabSystemExpandDramSize": "DRAM을 8GiB로 확장", "SettingsTabSystemIgnoreMissingServices": "누락된 서비스 무시", "SettingsTabGraphics": "그래픽", "SettingsTabGraphicsAPI": "그래픽 API", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "즐겨찾기 전환", "GameListContextMenuToggleFavoriteToolTip": "게임 즐겨찾기 상태 전환", "SettingsTabGeneralTheme": "테마:", + "SettingsTabGeneralThemeAuto": "자동", "SettingsTabGeneralThemeDark": "어두운 테마", "SettingsTabGeneralThemeLight": "밝은 테마", "ControllerSettingsConfigureGeneral": "구성", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "호스트 주소 공간의 메모리를 직접 매핑합니다. 훨씬 빠른 JIT 컴파일 및 실행합니다.", "MemoryManagerUnsafeTooltip": "메모리를 직접 매핑하지만 접속하기 전에 게스트 주소 공간 내의 주소를 마스킹하지 마십시오. 더 빠르지만 안전을 희생해야 합니다. 게스트 응용 프로그램은 Ryujinx의 어디에서나 메모리에 접속할 수 있으므로 이 모드에서는 신뢰할 수 있는 프로그램만 실행하세요.", "UseHypervisorTooltip": "JIT 대신 하이퍼바이저를 사용합니다. 하이퍼바이저를 사용할 수 있을 때 성능을 향상시키지만, 현재 상태에서는 불안정할 수 있습니다.", - "DRamTooltip": "대체 메모리모드 레이아웃을 활용하여 스위치 개발 모델을 모방합니다.\n\n고해상도 텍스처 팩 또는 4k 해상도 모드에만 유용합니다. 성능을 향상시키지 않습니다.\n\n확실하지 않으면 꺼 두세요.", + "DRamTooltip": "스위치 개발 모델을 모방하기 위해 8GiB DRAM을 사용하는 대체 메모리 모드를 활용합니다.\n\n이는 고해상도 텍스처 팩이나 4k 해상도 모드에만 유용합니다. 성능을 개선하지 않습니다.\n\n확실하지 않으면 꺼 두세요.", "IgnoreMissingServicesTooltip": "구현되지 않은 호라이즌 OS 서비스를 무시합니다. 이것은 특정 게임을 부팅할 때 충돌을 우회하는 데 도움이 될 수 있습니다.\n\n확실하지 않으면 꺼 두세요.", "GraphicsBackendThreadingTooltip": "두 번째 스레드에서 그래픽 백엔드 명령을 실행합니다.\n\n세이더 컴파일 속도를 높이고 끊김 현상을 줄이며 자체 멀티스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 멀티스레딩이 있는 드라이버에서 성능이 약간 향상되었습니다.\n\n잘 모르겠으면 자동으로 설정하세요.", "GalThreadingTooltip": "두 번째 스레드에서 그래픽 백엔드 명령을 실행합니다.\n\n세이더 컴파일 속도를 높이고 끊김 현상을 줄이며 자체 멀티스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 멀티스레딩이 있는 드라이버에서 성능이 약간 향상되었습니다.\n\n잘 모르겠으면 자동으로 설정하세요.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "설정 가이드 열기", "NoUpdate": "업데이트 없음", "TitleUpdateVersionLabel": "버전 {0}", + "TitleBundledUpdateVersionLabel": "번들 : 버전 {0}", + "TitleBundledDlcLabel": "번들 :", "RyujinxInfo": "Ryujinx - 정보", "RyujinxConfirm": "Ryujinx - 확인", "FileDialogAllTypes": "모든 유형", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "게임 렌더에 안티 앨리어싱을 적용합니다.\n\nFXAA는 대부분의 이미지를 뿌옇게 만들지만, SMAA는 들쭉날쭉한 모서리 부분들을 찾아 부드럽게 만듭니다.\n\nFSR 스케일링 필터와 같이 사용하는 것은 권장하지 않습니다.\n\n이 옵션은 게임이 구동중일 때에도 아래 Apply 버튼을 눌러서 변경할 수 있습니다; 설정 창을 게임 창 옆에 두고 사용자가 선호하는 옵션을 실험하여 고를 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 끄기를 권장드립니다.", "GraphicsAALabel": "안티 앨리어싱:", "GraphicsScalingFilterLabel": "스케일링 필터:", - "GraphicsScalingFilterTooltip": "해상도 스케일에 사용될 스케일링 필터를 선택하세요.\n\nBilinear는 3D 게임에서 잘 작동하며 안전한 기본값입니다.\n\nNearest는 픽셀 아트 게임에 추천합니다.\n\nFSR 1.0은 그저 샤프닝 필터임으로, FXAA나 SMAA와 같이 사용하는 것은 권장하지 않습니다.\n\n이 옵션은 게임이 구동중일 때에도 아래 Apply 버튼을 눌러서 변경할 수 있습니다; 설정 창을 게임 창 옆에 두고 사용자가 선호하는 옵션을 실험하여 고를 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 BILINEAR로 두세요.", + "GraphicsScalingFilterTooltip": "해상도 스케일을 사용할 때 적용될 스케일링 필터를 선택하세요.\n\nBilinear는 3D 게임에 적합하며 안전한 기본 옵션입니다.\n\nNearest는 픽셀 아트 게임에 권장됩니다.\n\nFSR 1.0은 단순히 선명화 필터일 뿐이며 FXAA 또는 SMAA와 함께 사용하는 것은 권장되지 않습니다.\n\n출력창보다 큰 해상도를 다운스케일링할 때는 영역 스케일링을 권장합니다. 2배 이상 다운스케일링할 때 슈퍼샘플링된 앤티앨리어싱 효과를 얻는 데 사용할 수 있습니다.\n\n이 옵션은 아래의 \"적용\"을 클릭하여 게임을 실행하는 동안 변경할 수 있습니다. 설정 창을 옆으로 옮겨 원하는 게임 모양을 찾을 때까지 실험하면 됩니다.\n\n확실하지 않으면 BILINEAR로 두세요.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "영역", "GraphicsScalingFilterLevelLabel": "수준", "GraphicsScalingFilterLevelTooltip": "FSR 1.0의 샤프닝 레벨을 설정하세요. 높을수록 더 또렷해집니다.", "SmaaLow": "SMAA 낮음", diff --git a/src/Ryujinx/Assets/Locales/no_NO.json b/src/Ryujinx/Assets/Locales/no_NO.json new file mode 100644 index 000000000..b3cab7f5f --- /dev/null +++ b/src/Ryujinx/Assets/Locales/no_NO.json @@ -0,0 +1,785 @@ +{ + "Language": "English (US)", + "MenuBarFileOpenApplet": "Open Applet", + "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode", + "SettingsTabInputDirectMouseAccess": "Direct Mouse Access", + "SettingsTabSystemMemoryManagerMode": "Memory Manager Mode:", + "SettingsTabSystemMemoryManagerModeSoftware": "Software", + "SettingsTabSystemMemoryManagerModeHost": "Host (fast)", + "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host Unchecked (fastest, unsafe)", + "SettingsTabSystemUseHypervisor": "Use Hypervisor", + "MenuBarFile": "_File", + "MenuBarFileOpenFromFile": "_Load Application From File", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", + "MenuBarFileOpenUnpacked": "Load _Unpacked Game", + "MenuBarFileOpenEmuFolder": "Open Ryujinx Folder", + "MenuBarFileOpenLogsFolder": "Open Logs Folder", + "MenuBarFileExit": "_Exit", + "MenuBarOptions": "_Options", + "MenuBarOptionsToggleFullscreen": "Toggle Fullscreen", + "MenuBarOptionsStartGamesInFullscreen": "Start Games in Fullscreen Mode", + "MenuBarOptionsStopEmulation": "Stop Emulation", + "MenuBarOptionsSettings": "_Settings", + "MenuBarOptionsManageUserProfiles": "_Manage User Profiles", + "MenuBarActions": "_Actions", + "MenuBarOptionsSimulateWakeUpMessage": "Simulate Wake-up message", + "MenuBarActionsScanAmiibo": "Scan An Amiibo", + "MenuBarTools": "_Tools", + "MenuBarToolsInstallFirmware": "Install Firmware", + "MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP", + "MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory", + "MenuBarToolsManageFileTypes": "Manage file types", + "MenuBarToolsInstallFileTypes": "Install file types", + "MenuBarToolsUninstallFileTypes": "Uninstall file types", + "MenuBarView": "_View", + "MenuBarViewWindow": "Window Size", + "MenuBarViewWindow720": "720p", + "MenuBarViewWindow1080": "1080p", + "MenuBarHelp": "_Help", + "MenuBarHelpCheckForUpdates": "Check for Updates", + "MenuBarHelpAbout": "About", + "MenuSearch": "Search...", + "GameListHeaderFavorite": "Fav", + "GameListHeaderIcon": "Icon", + "GameListHeaderApplication": "Name", + "GameListHeaderDeveloper": "Developer", + "GameListHeaderVersion": "Version", + "GameListHeaderTimePlayed": "Play Time", + "GameListHeaderLastPlayed": "Last Played", + "GameListHeaderFileExtension": "File Ext", + "GameListHeaderFileSize": "File Size", + "GameListHeaderPath": "Path", + "GameListContextMenuOpenUserSaveDirectory": "Open User Save Directory", + "GameListContextMenuOpenUserSaveDirectoryToolTip": "Opens the directory which contains Application's User Save", + "GameListContextMenuOpenDeviceSaveDirectory": "Open Device Save Directory", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Opens the directory which contains Application's Device Save", + "GameListContextMenuOpenBcatSaveDirectory": "Open BCAT Save Directory", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Opens the directory which contains Application's BCAT Save", + "GameListContextMenuManageTitleUpdates": "Manage Title Updates", + "GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window", + "GameListContextMenuManageDlc": "Manage DLC", + "GameListContextMenuManageDlcToolTip": "Opens the DLC management window", + "GameListContextMenuCacheManagement": "Cache Management", + "GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild", + "GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch", + "GameListContextMenuCacheManagementPurgeShaderCache": "Purge Shader Cache", + "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deletes Application's shader cache", + "GameListContextMenuCacheManagementOpenPptcDirectory": "Open PPTC Directory", + "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Opens the directory which contains Application's PPTC cache", + "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Open Shader Cache Directory", + "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Opens the directory which contains Application's shader cache", + "GameListContextMenuExtractData": "Extract Data", + "GameListContextMenuExtractDataExeFS": "ExeFS", + "GameListContextMenuExtractDataExeFSToolTip": "Extract the ExeFS section from Application's current config (including updates)", + "GameListContextMenuExtractDataRomFS": "RomFS", + "GameListContextMenuExtractDataRomFSToolTip": "Extract the RomFS section from Application's current config (including updates)", + "GameListContextMenuExtractDataLogo": "Logo", + "GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)", + "GameListContextMenuCreateShortcut": "Create Application Shortcut", + "GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application", + "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", + "GameListContextMenuOpenModsDirectory": "Open Mods Directory", + "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", + "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", + "StatusBarGamesLoaded": "{0}/{1} Games Loaded", + "StatusBarSystemVersion": "System Version: {0}", + "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", + "LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}", + "LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.", + "LinuxVmMaxMapCountDialogButtonUntilRestart": "Yes, until the next restart", + "LinuxVmMaxMapCountDialogButtonPersistent": "Yes, permanently", + "LinuxVmMaxMapCountWarningTextPrimary": "Max amount of memory mappings is lower than recommended.", + "LinuxVmMaxMapCountWarningTextSecondary": "The current value of vm.max_map_count ({0}) is lower than {1}. Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.\n\nYou might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that.", + "Settings": "Settings", + "SettingsTabGeneral": "User Interface", + "SettingsTabGeneralGeneral": "General", + "SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence", + "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", + "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", + "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralHideCursor": "Hide Cursor:", + "SettingsTabGeneralHideCursorNever": "Never", + "SettingsTabGeneralHideCursorOnIdle": "On Idle", + "SettingsTabGeneralHideCursorAlways": "Always", + "SettingsTabGeneralGameDirectories": "Game Directories", + "SettingsTabGeneralAdd": "Add", + "SettingsTabGeneralRemove": "Remove", + "SettingsTabSystem": "System", + "SettingsTabSystemCore": "Core", + "SettingsTabSystemSystemRegion": "System Region:", + "SettingsTabSystemSystemRegionJapan": "Japan", + "SettingsTabSystemSystemRegionUSA": "USA", + "SettingsTabSystemSystemRegionEurope": "Europe", + "SettingsTabSystemSystemRegionAustralia": "Australia", + "SettingsTabSystemSystemRegionChina": "China", + "SettingsTabSystemSystemRegionKorea": "Korea", + "SettingsTabSystemSystemRegionTaiwan": "Taiwan", + "SettingsTabSystemSystemLanguage": "System Language:", + "SettingsTabSystemSystemLanguageJapanese": "Japanese", + "SettingsTabSystemSystemLanguageAmericanEnglish": "American English", + "SettingsTabSystemSystemLanguageFrench": "French", + "SettingsTabSystemSystemLanguageGerman": "German", + "SettingsTabSystemSystemLanguageItalian": "Italian", + "SettingsTabSystemSystemLanguageSpanish": "Spanish", + "SettingsTabSystemSystemLanguageChinese": "Chinese", + "SettingsTabSystemSystemLanguageKorean": "Korean", + "SettingsTabSystemSystemLanguageDutch": "Dutch", + "SettingsTabSystemSystemLanguagePortuguese": "Portuguese", + "SettingsTabSystemSystemLanguageRussian": "Russian", + "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanese", + "SettingsTabSystemSystemLanguageBritishEnglish": "British English", + "SettingsTabSystemSystemLanguageCanadianFrench": "Canadian French", + "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish", + "SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese", + "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese", + "SettingsTabSystemSystemTimeZone": "System TimeZone:", + "SettingsTabSystemSystemTime": "System Time:", + "SettingsTabSystemEnableVsync": "VSync", + "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", + "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", + "SettingsTabSystemAudioBackend": "Audio Backend:", + "SettingsTabSystemAudioBackendDummy": "Dummy", + "SettingsTabSystemAudioBackendOpenAL": "OpenAL", + "SettingsTabSystemAudioBackendSoundIO": "SoundIO", + "SettingsTabSystemAudioBackendSDL2": "SDL2", + "SettingsTabSystemHacks": "Hacks", + "SettingsTabSystemHacksNote": "May cause instability", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", + "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", + "SettingsTabGraphics": "Graphics", + "SettingsTabGraphicsAPI": "Graphics API", + "SettingsTabGraphicsEnableShaderCache": "Enable Shader Cache", + "SettingsTabGraphicsAnisotropicFiltering": "Anisotropic Filtering:", + "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", + "SettingsTabGraphicsAnisotropicFiltering2x": "2x", + "SettingsTabGraphicsAnisotropicFiltering4x": "4x", + "SettingsTabGraphicsAnisotropicFiltering8x": "8x", + "SettingsTabGraphicsAnisotropicFiltering16x": "16x", + "SettingsTabGraphicsResolutionScale": "Resolution Scale:", + "SettingsTabGraphicsResolutionScaleCustom": "Custom (Not recommended)", + "SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)", + "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", + "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", + "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)", + "SettingsTabGraphicsAspectRatio": "Aspect Ratio:", + "SettingsTabGraphicsAspectRatio4x3": "4:3", + "SettingsTabGraphicsAspectRatio16x9": "16:9", + "SettingsTabGraphicsAspectRatio16x10": "16:10", + "SettingsTabGraphicsAspectRatio21x9": "21:9", + "SettingsTabGraphicsAspectRatio32x9": "32:9", + "SettingsTabGraphicsAspectRatioStretch": "Stretch to Fit Window", + "SettingsTabGraphicsDeveloperOptions": "Developer Options", + "SettingsTabGraphicsShaderDumpPath": "Graphics Shader Dump Path:", + "SettingsTabLogging": "Logging", + "SettingsTabLoggingLogging": "Logging", + "SettingsTabLoggingEnableLoggingToFile": "Enable Logging to File", + "SettingsTabLoggingEnableStubLogs": "Enable Stub Logs", + "SettingsTabLoggingEnableInfoLogs": "Enable Info Logs", + "SettingsTabLoggingEnableWarningLogs": "Enable Warning Logs", + "SettingsTabLoggingEnableErrorLogs": "Enable Error Logs", + "SettingsTabLoggingEnableTraceLogs": "Enable Trace Logs", + "SettingsTabLoggingEnableGuestLogs": "Enable Guest Logs", + "SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs", + "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:", + "SettingsTabLoggingDeveloperOptions": "Developer Options", + "SettingsTabLoggingDeveloperOptionsNote": "WARNING: Will reduce performance", + "SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:", + "SettingsTabLoggingGraphicsBackendLogLevelNone": "None", + "SettingsTabLoggingGraphicsBackendLogLevelError": "Error", + "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Slowdowns", + "SettingsTabLoggingGraphicsBackendLogLevelAll": "All", + "SettingsTabLoggingEnableDebugLogs": "Enable Debug Logs", + "SettingsTabInput": "Input", + "SettingsTabInputEnableDockedMode": "Docked Mode", + "SettingsTabInputDirectKeyboardAccess": "Direct Keyboard Access", + "SettingsButtonSave": "Save", + "SettingsButtonClose": "Close", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Cancel", + "SettingsButtonApply": "Apply", + "ControllerSettingsPlayer": "Player", + "ControllerSettingsPlayer1": "Player 1", + "ControllerSettingsPlayer2": "Player 2", + "ControllerSettingsPlayer3": "Player 3", + "ControllerSettingsPlayer4": "Player 4", + "ControllerSettingsPlayer5": "Player 5", + "ControllerSettingsPlayer6": "Player 6", + "ControllerSettingsPlayer7": "Player 7", + "ControllerSettingsPlayer8": "Player 8", + "ControllerSettingsHandheld": "Handheld", + "ControllerSettingsInputDevice": "Input Device", + "ControllerSettingsRefresh": "Refresh", + "ControllerSettingsDeviceDisabled": "Disabled", + "ControllerSettingsControllerType": "Controller Type", + "ControllerSettingsControllerTypeHandheld": "Handheld", + "ControllerSettingsControllerTypeProController": "Pro Controller", + "ControllerSettingsControllerTypeJoyConPair": "JoyCon Pair", + "ControllerSettingsControllerTypeJoyConLeft": "JoyCon Left", + "ControllerSettingsControllerTypeJoyConRight": "JoyCon Right", + "ControllerSettingsProfile": "Profile", + "ControllerSettingsProfileDefault": "Default", + "ControllerSettingsLoad": "Load", + "ControllerSettingsAdd": "Add", + "ControllerSettingsRemove": "Remove", + "ControllerSettingsButtons": "Buttons", + "ControllerSettingsButtonA": "A", + "ControllerSettingsButtonB": "B", + "ControllerSettingsButtonX": "X", + "ControllerSettingsButtonY": "Y", + "ControllerSettingsButtonPlus": "+", + "ControllerSettingsButtonMinus": "-", + "ControllerSettingsDPad": "Directional Pad", + "ControllerSettingsDPadUp": "Up", + "ControllerSettingsDPadDown": "Down", + "ControllerSettingsDPadLeft": "Left", + "ControllerSettingsDPadRight": "Right", + "ControllerSettingsStickButton": "Button", + "ControllerSettingsStickUp": "Up", + "ControllerSettingsStickDown": "Down", + "ControllerSettingsStickLeft": "Left", + "ControllerSettingsStickRight": "Right", + "ControllerSettingsStickStick": "Stick", + "ControllerSettingsStickInvertXAxis": "Invert Stick X", + "ControllerSettingsStickInvertYAxis": "Invert Stick Y", + "ControllerSettingsStickDeadzone": "Deadzone:", + "ControllerSettingsLStick": "Left Stick", + "ControllerSettingsRStick": "Right Stick", + "ControllerSettingsTriggersLeft": "Triggers Left", + "ControllerSettingsTriggersRight": "Triggers Right", + "ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left", + "ControllerSettingsTriggersButtonsRight": "Trigger Buttons Right", + "ControllerSettingsTriggers": "Triggers", + "ControllerSettingsTriggerL": "L", + "ControllerSettingsTriggerR": "R", + "ControllerSettingsTriggerZL": "ZL", + "ControllerSettingsTriggerZR": "ZR", + "ControllerSettingsLeftSL": "SL", + "ControllerSettingsLeftSR": "SR", + "ControllerSettingsRightSL": "SL", + "ControllerSettingsRightSR": "SR", + "ControllerSettingsExtraButtonsLeft": "Buttons Left", + "ControllerSettingsExtraButtonsRight": "Buttons Right", + "ControllerSettingsMisc": "Miscellaneous", + "ControllerSettingsTriggerThreshold": "Trigger Threshold:", + "ControllerSettingsMotion": "Motion", + "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Use CemuHook compatible motion", + "ControllerSettingsMotionControllerSlot": "Controller Slot:", + "ControllerSettingsMotionMirrorInput": "Mirror Input", + "ControllerSettingsMotionRightJoyConSlot": "Right JoyCon Slot:", + "ControllerSettingsMotionServerHost": "Server Host:", + "ControllerSettingsMotionGyroSensitivity": "Gyro Sensitivity:", + "ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:", + "ControllerSettingsSave": "Save", + "ControllerSettingsClose": "Close", + "KeyUnknown": "Unknown", + "KeyShiftLeft": "Shift Left", + "KeyShiftRight": "Shift Right", + "KeyControlLeft": "Ctrl Left", + "KeyMacControlLeft": "⌃ Left", + "KeyControlRight": "Ctrl Right", + "KeyMacControlRight": "⌃ Right", + "KeyAltLeft": "Alt Left", + "KeyMacAltLeft": "⌥ Left", + "KeyAltRight": "Alt Right", + "KeyMacAltRight": "⌥ Right", + "KeyWinLeft": "⊞ Left", + "KeyMacWinLeft": "⌘ Left", + "KeyWinRight": "⊞ Right", + "KeyMacWinRight": "⌘ Right", + "KeyMenu": "Menu", + "KeyUp": "Up", + "KeyDown": "Down", + "KeyLeft": "Left", + "KeyRight": "Right", + "KeyEnter": "Enter", + "KeyEscape": "Escape", + "KeySpace": "Space", + "KeyTab": "Tab", + "KeyBackSpace": "Backspace", + "KeyInsert": "Insert", + "KeyDelete": "Delete", + "KeyPageUp": "Page Up", + "KeyPageDown": "Page Down", + "KeyHome": "Home", + "KeyEnd": "End", + "KeyCapsLock": "Caps Lock", + "KeyScrollLock": "Scroll Lock", + "KeyPrintScreen": "Print Screen", + "KeyPause": "Pause", + "KeyNumLock": "Num Lock", + "KeyClear": "Clear", + "KeyKeypad0": "Keypad 0", + "KeyKeypad1": "Keypad 1", + "KeyKeypad2": "Keypad 2", + "KeyKeypad3": "Keypad 3", + "KeyKeypad4": "Keypad 4", + "KeyKeypad5": "Keypad 5", + "KeyKeypad6": "Keypad 6", + "KeyKeypad7": "Keypad 7", + "KeyKeypad8": "Keypad 8", + "KeyKeypad9": "Keypad 9", + "KeyKeypadDivide": "Keypad Divide", + "KeyKeypadMultiply": "Keypad Multiply", + "KeyKeypadSubtract": "Keypad Subtract", + "KeyKeypadAdd": "Keypad Add", + "KeyKeypadDecimal": "Keypad Decimal", + "KeyKeypadEnter": "Keypad Enter", + "KeyNumber0": "0", + "KeyNumber1": "1", + "KeyNumber2": "2", + "KeyNumber3": "3", + "KeyNumber4": "4", + "KeyNumber5": "5", + "KeyNumber6": "6", + "KeyNumber7": "7", + "KeyNumber8": "8", + "KeyNumber9": "9", + "KeyTilde": "~", + "KeyGrave": "`", + "KeyMinus": "-", + "KeyPlus": "+", + "KeyBracketLeft": "[", + "KeyBracketRight": "]", + "KeySemicolon": ";", + "KeyQuote": "\"", + "KeyComma": ",", + "KeyPeriod": ".", + "KeySlash": "/", + "KeyBackSlash": "\\", + "KeyUnbound": "Unbound", + "GamepadLeftStick": "L Stick Button", + "GamepadRightStick": "R Stick Button", + "GamepadLeftShoulder": "Left Shoulder", + "GamepadRightShoulder": "Right Shoulder", + "GamepadLeftTrigger": "Left Trigger", + "GamepadRightTrigger": "Right Trigger", + "GamepadDpadUp": "Up", + "GamepadDpadDown": "Down", + "GamepadDpadLeft": "Left", + "GamepadDpadRight": "Right", + "GamepadMinus": "-", + "GamepadPlus": "+", + "GamepadGuide": "Guide", + "GamepadMisc1": "Misc", + "GamepadPaddle1": "Paddle 1", + "GamepadPaddle2": "Paddle 2", + "GamepadPaddle3": "Paddle 3", + "GamepadPaddle4": "Paddle 4", + "GamepadTouchpad": "Touchpad", + "GamepadSingleLeftTrigger0": "Left Trigger 0", + "GamepadSingleRightTrigger0": "Right Trigger 0", + "GamepadSingleLeftTrigger1": "Left Trigger 1", + "GamepadSingleRightTrigger1": "Right Trigger 1", + "StickLeft": "Left Stick", + "StickRight": "Right Stick", + "UserProfilesSelectedUserProfile": "Selected User Profile:", + "UserProfilesSaveProfileName": "Save Profile Name", + "UserProfilesChangeProfileImage": "Change Profile Image", + "UserProfilesAvailableUserProfiles": "Available User Profiles:", + "UserProfilesAddNewProfile": "Create Profile", + "UserProfilesDelete": "Delete", + "UserProfilesClose": "Close", + "ProfileNameSelectionWatermark": "Choose a nickname", + "ProfileImageSelectionTitle": "Profile Image Selection", + "ProfileImageSelectionHeader": "Choose a profile Image", + "ProfileImageSelectionNote": "You may import a custom profile image, or select an avatar from system firmware", + "ProfileImageSelectionImportImage": "Import Image File", + "ProfileImageSelectionSelectAvatar": "Select Firmware Avatar", + "InputDialogTitle": "Input Dialog", + "InputDialogOk": "OK", + "InputDialogCancel": "Cancel", + "InputDialogAddNewProfileTitle": "Choose the Profile Name", + "InputDialogAddNewProfileHeader": "Please Enter a Profile Name", + "InputDialogAddNewProfileSubtext": "(Max Length: {0})", + "AvatarChoose": "Choose Avatar", + "AvatarSetBackgroundColor": "Set Background Color", + "AvatarClose": "Close", + "ControllerSettingsLoadProfileToolTip": "Load Profile", + "ControllerSettingsAddProfileToolTip": "Add Profile", + "ControllerSettingsRemoveProfileToolTip": "Remove Profile", + "ControllerSettingsSaveProfileToolTip": "Save Profile", + "MenuBarFileToolsTakeScreenshot": "Take Screenshot", + "MenuBarFileToolsHideUi": "Hide UI", + "GameListContextMenuRunApplication": "Run Application", + "GameListContextMenuToggleFavorite": "Toggle Favorite", + "GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game", + "SettingsTabGeneralTheme": "Theme:", + "SettingsTabGeneralThemeAuto": "Auto", + "SettingsTabGeneralThemeDark": "Dark", + "SettingsTabGeneralThemeLight": "Light", + "ControllerSettingsConfigureGeneral": "Configure", + "ControllerSettingsRumble": "Rumble", + "ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier", + "ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier", + "DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]", + "DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?", + "DialogConfirmationTitle": "Ryujinx - Confirmation", + "DialogUpdaterTitle": "Ryujinx - Updater", + "DialogErrorTitle": "Ryujinx - Error", + "DialogWarningTitle": "Ryujinx - Warning", + "DialogExitTitle": "Ryujinx - Exit", + "DialogErrorMessage": "Ryujinx has encountered an error", + "DialogExitMessage": "Are you sure you want to close Ryujinx?", + "DialogExitSubMessage": "All unsaved data will be lost!", + "DialogMessageCreateSaveErrorMessage": "There was an error creating the specified savedata: {0}", + "DialogMessageFindSaveErrorMessage": "There was an error finding the specified savedata: {0}", + "FolderDialogExtractTitle": "Choose the folder to extract into", + "DialogNcaExtractionMessage": "Extracting {0} section from {1}...", + "DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor", + "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraction failure. The main NCA was not present in the selected file.", + "DialogNcaExtractionCheckLogErrorMessage": "Extraction failure. Read the log file for further information.", + "DialogNcaExtractionSuccessMessage": "Extraction completed successfully.", + "DialogUpdaterConvertFailedMessage": "Failed to convert the current Ryujinx version.", + "DialogUpdaterCancelUpdateMessage": "Cancelling Update!", + "DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the most updated version of Ryujinx!", + "DialogUpdaterFailedToGetVersionMessage": "An error has occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.", + "DialogUpdaterConvertFailedGithubMessage": "Failed to convert the received Ryujinx version from Github Release.", + "DialogUpdaterDownloadingMessage": "Downloading Update...", + "DialogUpdaterExtractionMessage": "Extracting Update...", + "DialogUpdaterRenamingMessage": "Renaming Update...", + "DialogUpdaterAddingFilesMessage": "Adding New Update...", + "DialogUpdaterCompleteMessage": "Update Complete!", + "DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?", + "DialogUpdaterNoInternetMessage": "You are not connected to the Internet!", + "DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!", + "DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!", + "DialogUpdaterDirtyBuildSubMessage": "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version.", + "DialogRestartRequiredMessage": "Restart Required", + "DialogThemeRestartMessage": "Theme has been saved. A restart is needed to apply the theme.", + "DialogThemeRestartSubMessage": "Do you want to restart", + "DialogFirmwareInstallEmbeddedMessage": "Would you like to install the firmware embedded in this game? (Firmware {0})", + "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", + "DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed", + "DialogFirmwareInstalledMessage": "Firmware {0} was installed", + "DialogInstallFileTypesSuccessMessage": "Successfully installed file types!", + "DialogInstallFileTypesErrorMessage": "Failed to install file types.", + "DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!", + "DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.", + "DialogOpenSettingsWindowLabel": "Open Settings Window", + "DialogControllerAppletTitle": "Controller Applet", + "DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}", + "DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}", + "DialogErrorAppletErrorExceptionMessage": "Error displaying ErrorApplet Dialog: {0}", + "DialogUserErrorDialogMessage": "{0}: {1}", + "DialogUserErrorDialogInfoMessage": "\nFor more information on how to fix this error, follow our Setup Guide.", + "DialogUserErrorDialogTitle": "Ryujinx Error ({0})", + "DialogAmiiboApiTitle": "Amiibo API", + "DialogAmiiboApiFailFetchMessage": "An error occured while fetching information from the API.", + "DialogAmiiboApiConnectErrorMessage": "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.", + "DialogProfileInvalidProfileErrorMessage": "Profile {0} is incompatible with the current input configuration system.", + "DialogProfileDefaultProfileOverwriteErrorMessage": "Default Profile can not be overwritten", + "DialogProfileDeleteProfileTitle": "Deleting Profile", + "DialogProfileDeleteProfileMessage": "This action is irreversible, are you sure you want to continue?", + "DialogWarning": "Warning", + "DialogPPTCDeletionMessage": "You are about to queue a PPTC rebuild on the next boot of:\n\n{0}\n\nAre you sure you want to proceed?", + "DialogPPTCDeletionErrorMessage": "Error purging PPTC cache at {0}: {1}", + "DialogShaderDeletionMessage": "You are about to delete the Shader cache for :\n\n{0}\n\nAre you sure you want to proceed?", + "DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}", + "DialogRyujinxErrorMessage": "Ryujinx has encountered an error", + "DialogInvalidTitleIdErrorMessage": "UI error: The selected game did not have a valid title ID", + "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "A valid system firmware was not found in {0}.", + "DialogFirmwareInstallerFirmwareInstallTitle": "Install Firmware {0}", + "DialogFirmwareInstallerFirmwareInstallMessage": "System version {0} will be installed.", + "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nThis will replace the current system version {0}.", + "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nDo you want to continue?", + "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installing firmware...", + "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "System version {0} successfully installed.", + "DialogUserProfileDeletionWarningMessage": "There would be no other profiles to be opened if selected profile is deleted", + "DialogUserProfileDeletionConfirmMessage": "Do you want to delete the selected profile", + "DialogUserProfileUnsavedChangesTitle": "Warning - Unsaved Changes", + "DialogUserProfileUnsavedChangesMessage": "You have made changes to this user profile that have not been saved.", + "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", + "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", + "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", + "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", + "DialogModAlreadyExistsMessage": "Mod already exists", + "DialogModInvalidMessage": "The specified directory does not contain a mod!", + "DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!", + "DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!", + "DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.", + "DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", + "DialogPerformanceCheckShaderDumpEnabledMessage": "You have shader dumping enabled, which is designed to be used by developers only.", + "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", + "DialogLoadAppGameAlreadyLoadedMessage": "A game has already been loaded", + "DialogLoadAppGameAlreadyLoadedSubMessage": "Please stop emulation or close the emulator before launching another game.", + "DialogUpdateAddUpdateErrorMessage": "The specified file does not contain an update for the selected title!", + "DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading", + "DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.", + "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", + "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", + "SettingsTabGraphicsFeaturesOptions": "Features", + "SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:", + "CommonAuto": "Auto", + "CommonOff": "Off", + "CommonOn": "On", + "InputDialogYes": "Yes", + "InputDialogNo": "No", + "DialogProfileInvalidProfileNameErrorMessage": "The file name contains invalid characters. Please try again.", + "MenuBarOptionsPauseEmulation": "Pause", + "MenuBarOptionsResumeEmulation": "Resume", + "AboutUrlTooltipMessage": "Click to open the Ryujinx website in your default browser.", + "AboutDisclaimerMessage": "Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.", + "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.", + "AboutPatreonUrlTooltipMessage": "Click to open the Ryujinx Patreon page in your default browser.", + "AboutGithubUrlTooltipMessage": "Click to open the Ryujinx GitHub page in your default browser.", + "AboutDiscordUrlTooltipMessage": "Click to open an invite to the Ryujinx Discord server in your default browser.", + "AboutTwitterUrlTooltipMessage": "Click to open the Ryujinx Twitter page in your default browser.", + "AboutRyujinxAboutTitle": "About:", + "AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nPlease support us on Patreon.\nGet all the latest news on our Twitter or Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.", + "AboutRyujinxMaintainersTitle": "Maintained By:", + "AboutRyujinxMaintainersContentTooltipMessage": "Click to open the Contributors page in your default browser.", + "AboutRyujinxSupprtersTitle": "Supported on Patreon By:", + "AmiiboSeriesLabel": "Amiibo Series", + "AmiiboCharacterLabel": "Character", + "AmiiboScanButtonLabel": "Scan It", + "AmiiboOptionsShowAllLabel": "Show All Amiibo", + "AmiiboOptionsUsRandomTagLabel": "Hack: Use Random tag Uuid", + "DlcManagerTableHeadingEnabledLabel": "Enabled", + "DlcManagerTableHeadingTitleIdLabel": "Title ID", + "DlcManagerTableHeadingContainerPathLabel": "Container Path", + "DlcManagerTableHeadingFullPathLabel": "Full Path", + "DlcManagerRemoveAllButton": "Remove All", + "DlcManagerEnableAllButton": "Enable All", + "DlcManagerDisableAllButton": "Disable All", + "ModManagerDeleteAllButton": "Delete All", + "MenuBarOptionsChangeLanguage": "Change Language", + "MenuBarShowFileTypes": "Show File Types", + "CommonSort": "Sort", + "CommonShowNames": "Show Names", + "CommonFavorite": "Favorite", + "OrderAscending": "Ascending", + "OrderDescending": "Descending", + "SettingsTabGraphicsFeatures": "Features & Enhancements", + "ErrorWindowTitle": "Error Window", + "ToggleDiscordTooltip": "Choose whether or not to display Ryujinx on your \"currently playing\" Discord activity", + "AddGameDirBoxTooltip": "Enter a game directory to add to the list", + "AddGameDirTooltip": "Add a game directory to the list", + "RemoveGameDirTooltip": "Remove selected game directory", + "CustomThemeCheckTooltip": "Use a custom Avalonia theme for the GUI to change the appearance of the emulator menus", + "CustomThemePathTooltip": "Path to custom GUI theme", + "CustomThemeBrowseTooltip": "Browse for a custom GUI theme", + "DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.", + "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", + "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", + "RegionTooltip": "Change System Region", + "LanguageTooltip": "Change System Language", + "TimezoneTooltip": "Change System TimeZone", + "TimeTooltip": "Change System Time", + "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", + "PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", + "FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.", + "AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", + "MemoryManagerTooltip": "Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance.\n\nSet to HOST UNCHECKED if unsure.", + "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", + "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", + "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", + "UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", + "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", + "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", + "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", + "ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.", + "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", + "ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.", + "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", + "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", + "ShaderDumpPathTooltip": "Graphics Shaders Dump Path", + "FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.", + "StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.", + "InfoLogTooltip": "Prints info log messages in the console. Does not affect performance.", + "WarnLogTooltip": "Prints warning log messages in the console. Does not affect performance.", + "ErrorLogTooltip": "Prints error log messages in the console. Does not affect performance.", + "TraceLogTooltip": "Prints trace log messages in the console. Does not affect performance.", + "GuestLogTooltip": "Prints guest log messages in the console. Does not affect performance.", + "FileAccessLogTooltip": "Prints file access log messages in the console.", + "FSAccessLogModeTooltip": "Enables FS access log output to the console. Possible modes are 0-3", + "DeveloperOptionTooltip": "Use with care", + "OpenGlLogLevel": "Requires appropriate log levels enabled", + "DebugLogTooltip": "Prints debug log messages in the console.\n\nOnly use this if specifically instructed by a staff member, as it will make logs difficult to read and worsen emulator performance.", + "LoadApplicationFileTooltip": "Open a file explorer to choose a Switch compatible file to load", + "LoadApplicationFolderTooltip": "Open a file explorer to choose a Switch compatible, unpacked application to load", + "OpenRyujinxFolderTooltip": "Open Ryujinx filesystem folder", + "OpenRyujinxLogsTooltip": "Opens the folder where logs are written to", + "ExitTooltip": "Exit Ryujinx", + "OpenSettingsTooltip": "Open settings window", + "OpenProfileManagerTooltip": "Open User Profiles Manager window", + "StopEmulationTooltip": "Stop emulation of the current game and return to game selection", + "CheckUpdatesTooltip": "Check for updates to Ryujinx", + "OpenAboutTooltip": "Open About Window", + "GridSize": "Grid Size", + "GridSizeTooltip": "Change the size of grid items", + "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brazilian Portuguese", + "AboutRyujinxContributorsButtonHeader": "See All Contributors", + "SettingsTabSystemAudioVolume": "Volume: ", + "AudioVolumeTooltip": "Change Audio Volume", + "SettingsTabSystemEnableInternetAccess": "Guest Internet Access/LAN Mode", + "EnableInternetAccessTooltip": "Allows the emulated application to connect to the Internet.\n\nGames with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well.\n\nDoes NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet.\n\nLeave OFF if unsure.", + "GameListContextMenuManageCheatToolTip": "Manage Cheats", + "GameListContextMenuManageCheat": "Manage Cheats", + "GameListContextMenuManageModToolTip": "Manage Mods", + "GameListContextMenuManageMod": "Manage Mods", + "ControllerSettingsStickRange": "Range:", + "DialogStopEmulationTitle": "Ryujinx - Stop Emulation", + "DialogStopEmulationMessage": "Are you sure you want to stop emulation?", + "SettingsTabCpu": "CPU", + "SettingsTabAudio": "Audio", + "SettingsTabNetwork": "Network", + "SettingsTabNetworkConnection": "Network Connection", + "SettingsTabCpuCache": "CPU Cache", + "SettingsTabCpuMemory": "CPU Mode", + "DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.", + "UpdaterDisabledWarningTitle": "Updater Disabled!", + "ControllerSettingsRotate90": "Rotate 90° Clockwise", + "IconSize": "Icon Size", + "IconSizeTooltip": "Change the size of game icons", + "MenuBarOptionsShowConsole": "Show Console", + "ShaderCachePurgeError": "Error purging shader cache at {0}: {1}", + "UserErrorNoKeys": "Keys not found", + "UserErrorNoFirmware": "Firmware not found", + "UserErrorFirmwareParsingFailed": "Firmware parsing error", + "UserErrorApplicationNotFound": "Application not found", + "UserErrorUnknown": "Unknown error", + "UserErrorUndefined": "Undefined error", + "UserErrorNoKeysDescription": "Ryujinx was unable to find your 'prod.keys' file", + "UserErrorNoFirmwareDescription": "Ryujinx was unable to find any firmwares installed", + "UserErrorFirmwareParsingFailedDescription": "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.", + "UserErrorApplicationNotFoundDescription": "Ryujinx couldn't find a valid application at the given path.", + "UserErrorUnknownDescription": "An unknown error occured!", + "UserErrorUndefinedDescription": "An undefined error occured! This shouldn't happen, please contact a dev!", + "OpenSetupGuideMessage": "Open the Setup Guide", + "NoUpdate": "No Update", + "TitleUpdateVersionLabel": "Version {0}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", + "RyujinxInfo": "Ryujinx - Info", + "RyujinxConfirm": "Ryujinx - Confirmation", + "FileDialogAllTypes": "All types", + "Never": "Never", + "SwkbdMinCharacters": "Must be at least {0} characters long", + "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", + "SoftwareKeyboard": "Software Keyboard", + "SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only", + "SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only", + "SoftwareKeyboardModeASCII": "Must be ASCII text only", + "ControllerAppletControllers": "Supported Controllers:", + "ControllerAppletPlayers": "Players:", + "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.", + "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", + "UpdaterRenaming": "Renaming Old Files...", + "UpdaterRenameFailed": "Updater was unable to rename file: {0}", + "UpdaterAddingFiles": "Adding New Files...", + "UpdaterExtracting": "Extracting Update...", + "UpdaterDownloading": "Downloading Update...", + "Game": "Game", + "Docked": "Docked", + "Handheld": "Handheld", + "ConnectionError": "Connection Error.", + "AboutPageDeveloperListMore": "{0} and more...", + "ApiError": "API Error.", + "LoadingHeading": "Loading {0}", + "CompilingPPTC": "Compiling PTC", + "CompilingShaders": "Compiling Shaders", + "AllKeyboards": "All keyboards", + "OpenFileDialogTitle": "Select a supported file to open", + "OpenFolderDialogTitle": "Select a folder with an unpacked game", + "AllSupportedFormats": "All Supported Formats", + "RyujinxUpdater": "Ryujinx Updater", + "SettingsTabHotkeys": "Keyboard Hotkeys", + "SettingsTabHotkeysHotkeys": "Keyboard Hotkeys", + "SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:", + "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", + "SettingsTabHotkeysShowUiHotkey": "Show UI:", + "SettingsTabHotkeysPauseHotkey": "Pause:", + "SettingsTabHotkeysToggleMuteHotkey": "Mute:", + "ControllerMotionTitle": "Motion Control Settings", + "ControllerRumbleTitle": "Rumble Settings", + "SettingsSelectThemeFileDialogTitle": "Select Theme File", + "SettingsXamlThemeFile": "Xaml Theme File", + "AvatarWindowTitle": "Manage Accounts - Avatar", + "Amiibo": "Amiibo", + "Unknown": "Unknown", + "Usage": "Usage", + "Writable": "Writable", + "SelectDlcDialogTitle": "Select DLC files", + "SelectUpdateDialogTitle": "Select update files", + "SelectModDialogTitle": "Select mod directory", + "UserProfileWindowTitle": "User Profiles Manager", + "CheatWindowTitle": "Cheats Manager", + "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", + "ModWindowTitle": "Manage Mods for {0} ({1})", + "UpdateWindowTitle": "Title Update Manager", + "CheatWindowHeading": "Cheats Available for {0} [{1}]", + "BuildId": "BuildId:", + "DlcWindowHeading": "{0} Downloadable Content(s)", + "ModWindowHeading": "{0} Mod(s)", + "UserProfilesEditProfile": "Edit Selected", + "Cancel": "Cancel", + "Save": "Save", + "Discard": "Discard", + "Paused": "Paused", + "UserProfilesSetProfileImage": "Set Profile Image", + "UserProfileEmptyNameError": "Name is required", + "UserProfileNoImageError": "Profile image must be set", + "GameUpdateWindowHeading": "Manage Updates for {0} ({1})", + "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", + "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", + "UserProfilesName": "Name:", + "UserProfilesUserId": "User ID:", + "SettingsTabGraphicsBackend": "Graphics Backend", + "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", + "SettingsEnableTextureRecompression": "Enable Texture Recompression", + "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", + "SettingsTabGraphicsPreferredGpu": "Preferred GPU", + "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", + "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", + "SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied", + "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", + "RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?", + "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", + "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", + "SettingsEnableMacroHLE": "Enable Macro HLE", + "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.", + "SettingsEnableColorSpacePassthrough": "Color Space Passthrough", + "SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.", + "VolumeShort": "Vol", + "UserProfilesManageSaves": "Manage Saves", + "DeleteUserSave": "Do you want to delete user save for this game?", + "IrreversibleActionNote": "This action is not reversible.", + "SaveManagerHeading": "Manage Saves for {0} ({1})", + "SaveManagerTitle": "Save Manager", + "Name": "Name", + "Size": "Size", + "Search": "Search", + "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", + "Recover": "Recover", + "UserProfilesRecoverHeading": "Saves were found for the following accounts", + "UserProfilesRecoverEmptyList": "No profiles to recover", + "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", + "GraphicsAALabel": "Anti-Aliasing:", + "GraphicsScalingFilterLabel": "Scaling Filter:", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterBilinear": "Bilinear", + "GraphicsScalingFilterNearest": "Nearest", + "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", + "GraphicsScalingFilterLevelLabel": "Level", + "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", + "SmaaLow": "SMAA Low", + "SmaaMedium": "SMAA Medium", + "SmaaHigh": "SMAA High", + "SmaaUltra": "SMAA Ultra", + "UserEditorTitle": "Edit User", + "UserEditorTitleCreate": "Create User", + "SettingsTabNetworkInterface": "Network Interface:", + "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", + "NetworkInterfaceDefault": "Default", + "PackagingShaders": "Packaging Shaders", + "AboutChangelogButton": "View Changelog on GitHub", + "AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.", + "SettingsTabNetworkMultiplayer": "Multiplayer", + "MultiplayerMode": "Mode:", + "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", + "MultiplayerModeDisabled": "Disabled", + "MultiplayerModeLdnMitm": "ldn_mitm" +} diff --git a/src/Ryujinx/Assets/Locales/pl_PL.json b/src/Ryujinx/Assets/Locales/pl_PL.json index 9d1bd7b44..a09643293 100644 --- a/src/Ryujinx/Assets/Locales/pl_PL.json +++ b/src/Ryujinx/Assets/Locales/pl_PL.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Użyj Hipernadzorcy", "MenuBarFile": "_Plik", "MenuBarFileOpenFromFile": "_Załaduj aplikację z pliku", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "Załaduj _rozpakowaną grę", "MenuBarFileOpenEmuFolder": "Otwórz folder Ryujinx", "MenuBarFileOpenLogsFolder": "Otwórz folder plików dziennika zdarzeń", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Zarządzaj rodzajami plików", "MenuBarToolsInstallFileTypes": "Typy plików instalacyjnych", "MenuBarToolsUninstallFileTypes": "Typy plików dezinstalacyjnych", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Widok", + "MenuBarViewWindow": "Rozmiar okna", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Pomoc", @@ -58,7 +59,7 @@ "GameListContextMenuManageTitleUpdatesToolTip": "Otwiera okno zarządzania aktualizacjami danej aplikacji", "GameListContextMenuManageDlc": "Zarządzaj dodatkową zawartością (DLC)", "GameListContextMenuManageDlcToolTip": "Otwiera okno zarządzania dodatkową zawartością", - "GameListContextMenuCacheManagement": "Zarządzanie Cache", + "GameListContextMenuCacheManagement": "Zarządzanie pamięcią podreczną", "GameListContextMenuCacheManagementPurgePptc": "Zakolejkuj rekompilację PPTC", "GameListContextMenuCacheManagementPurgePptcToolTip": "Zainicjuj Rekompilację PPTC przy następnym uruchomieniu gry", "GameListContextMenuCacheManagementPurgeShaderCache": "Wyczyść pamięć podręczną cieni", @@ -93,10 +94,10 @@ "Settings": "Ustawienia", "SettingsTabGeneral": "Interfejs użytkownika", "SettingsTabGeneralGeneral": "Ogólne", - "SettingsTabGeneralEnableDiscordRichPresence": "Włącz Bogatą Obecność Discord", + "SettingsTabGeneralEnableDiscordRichPresence": "Włącz Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Sprawdzaj aktualizacje przy uruchomieniu", "SettingsTabGeneralShowConfirmExitDialog": "Pokazuj okno dialogowe \"Potwierdź wyjście\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Pamiętaj rozmiar/pozycję okna", "SettingsTabGeneralHideCursor": "Ukryj kursor:", "SettingsTabGeneralHideCursorNever": "Nigdy", "SettingsTabGeneralHideCursorOnIdle": "Gdy bezczynny", @@ -137,14 +138,14 @@ "SettingsTabSystemEnableVsync": "Synchronizacja pionowa", "SettingsTabSystemEnablePptc": "PPTC (Profilowana pamięć podręczna trwałych łłumaczeń)", "SettingsTabSystemEnableFsIntegrityChecks": "Sprawdzanie integralności systemu plików", - "SettingsTabSystemAudioBackend": "Backend Dżwięku:", + "SettingsTabSystemAudioBackend": "Sterownik dżwięku:", "SettingsTabSystemAudioBackendDummy": "Atrapa", "SettingsTabSystemAudioBackendOpenAL": "OpenAL", "SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacki", "SettingsTabSystemHacksNote": " (mogą powodować niestabilność)", - "SettingsTabSystemExpandDramSize": "Użyj alternatywnego układu pamięci (Deweloperzy)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignoruj Brakujące Usługi", "SettingsTabGraphics": "Grafika", "SettingsTabGraphicsAPI": "Graficzne API", @@ -242,8 +243,8 @@ "ControllerSettingsStickInvertXAxis": "Odwróć gałkę X", "ControllerSettingsStickInvertYAxis": "Odwróć gałkę Y", "ControllerSettingsStickDeadzone": "Martwa strefa:", - "ControllerSettingsLStick": "Lewa Gałka", - "ControllerSettingsRStick": "Prawa Gałka", + "ControllerSettingsLStick": "Lewa gałka", + "ControllerSettingsRStick": "Prawa gałka", "ControllerSettingsTriggersLeft": "Lewe Triggery", "ControllerSettingsTriggersRight": "Prawe Triggery", "ControllerSettingsTriggersButtonsLeft": "Lewe Przyciski Triggerów", @@ -271,29 +272,29 @@ "ControllerSettingsMotionGyroDeadzone": "Deadzone Żyroskopu:", "ControllerSettingsSave": "Zapisz", "ControllerSettingsClose": "Zamknij", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", + "KeyUnknown": "Nieznane", + "KeyShiftLeft": "Lewy Shift ", + "KeyShiftRight": "Prawy Shift ", + "KeyControlLeft": "Lewy Ctrl ", + "KeyMacControlLeft": "Lewy Ctrl (⌃)", + "KeyControlRight": "Prawy Ctrl ", + "KeyMacControlRight": "Prawy Ctrl (⌃)", + "KeyAltLeft": "Lewy alt", + "KeyMacAltLeft": "Lewy Option (⌥)", + "KeyAltRight": "Lewy Alt", + "KeyMacAltRight": "Prawy Option (⌥)", "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", + "KeyMacWinLeft": "Lewy Command (⌘)", "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", + "KeyMacWinRight": "Prawy Command (⌘)", "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyUp": "Góra", + "KeyDown": "Dół", + "KeyLeft": "Lewo", + "KeyRight": "Prawo", "KeyEnter": "Enter", "KeyEscape": "Escape", - "KeySpace": "Space", + "KeySpace": "Spacja", "KeyTab": "Tab", "KeyBackSpace": "Backspace", "KeyInsert": "Insert", @@ -307,21 +308,21 @@ "KeyPrintScreen": "Print Screen", "KeyPause": "Pause", "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", + "KeyClear": "Wyczyść", + "KeyKeypad0": "Klawiatura numeryczna - 0", + "KeyKeypad1": "Klawiatura numeryczna - 1", + "KeyKeypad2": "Klawiatura numeryczna - 2", + "KeyKeypad3": "Klawiatura numeryczna - 3", + "KeyKeypad4": "Klawiatura numeryczna - 4", + "KeyKeypad5": "Klawiatura numeryczna - 5", + "KeyKeypad6": "Klawiatura numeryczna - 6", + "KeyKeypad7": "Klawiatura numeryczna - 7", + "KeyKeypad8": "Klawiatura numeryczna - 8", + "KeyKeypad9": "Klawiatura numeryczna - 9", + "KeyKeypadDivide": "Klawiatura numeryczna - znak dzielenia", + "KeyKeypadMultiply": "Klawiatura numeryczna - znak mnożenia", "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", + "KeyKeypadAdd": "Klawiatura numeryczna - Dodawanie", "KeyKeypadDecimal": "Keypad Decimal", "KeyKeypadEnter": "Keypad Enter", "KeyNumber0": "0", @@ -346,17 +347,17 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "KeyUnbound": "Nieprzypisany", + "GamepadLeftStick": "Przycisk lewej gałki", + "GamepadRightStick": "Przycisk prawej gałki", + "GamepadLeftShoulder": "Lewy bumper", + "GamepadRightShoulder": "Prawy bumper", + "GamepadLeftTrigger": "Lewy spust", + "GamepadRightTrigger": "Prawy spust", + "GamepadDpadUp": "Góra ", + "GamepadDpadDown": "Dół ", + "GamepadDpadLeft": "Lewo", + "GamepadDpadRight": "Prawo", "GamepadMinus": "-", "GamepadPlus": "+", "GamepadGuide": "Guide", @@ -365,13 +366,13 @@ "GamepadPaddle2": "Paddle 2", "GamepadPaddle3": "Paddle 3", "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "GamepadTouchpad": "Panel dotykowy", + "GamepadSingleLeftTrigger0": "Lewy spust 0", + "GamepadSingleRightTrigger0": "Prawy spust 0", + "GamepadSingleLeftTrigger1": "Lewy spust 1", + "GamepadSingleRightTrigger1": "Prawy spust 1", + "StickLeft": "Lewa gałka", + "StickRight": "Prawa gałka", "UserProfilesSelectedUserProfile": "Wybrany profil użytkownika:", "UserProfilesSaveProfileName": "Zapisz nazwę profilu", "UserProfilesChangeProfileImage": "Zmień obrazek profilu", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Przełącz na ulubione", "GameListContextMenuToggleFavoriteToolTip": "Przełącz status Ulubionej Gry", "SettingsTabGeneralTheme": "Motyw:", + "SettingsTabGeneralThemeAuto": "Automatyczne", "SettingsTabGeneralThemeDark": "Ciemny", "SettingsTabGeneralThemeLight": "Jasny", "ControllerSettingsConfigureGeneral": "Konfiguruj", @@ -568,12 +570,12 @@ "PptcToggleTooltip": "Zapisuje przetłumaczone funkcje JIT, dzięki czemu nie muszą być tłumaczone za każdym razem, gdy gra się ładuje.\n\nZmniejsza zacinanie się i znacznie przyspiesza uruchamianie po pierwszym uruchomieniu gry.\n\nJeśli nie masz pewności, pozostaw WŁĄCZONE", "FsIntegrityToggleTooltip": "Sprawdza pliki podczas uruchamiania gry i jeśli zostaną wykryte uszkodzone pliki, wyświetla w dzienniku błąd hash.\n\nNie ma wpływu na wydajność i ma pomóc w rozwiązywaniu problemów.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", "AudioBackendTooltip": "Zmienia backend używany do renderowania dźwięku.\n\nSDL2 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL2.", - "MemoryManagerTooltip": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.", + "MemoryManagerTooltip": "Zmień sposób przypisywania i uzyskiwania dostępu do pamięci gościa. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na \"Gospodarza (NIESPRAWDZONY)\", jeśli nie masz pewności.", "MemoryManagerSoftwareTooltip": "Użyj tabeli stron oprogramowania do translacji adresów. Najwyższa celność, ale najwolniejsza wydajność.", "MemoryManagerHostTooltip": "Bezpośrednio mapuj pamięć w przestrzeni adresowej hosta. Znacznie szybsza kompilacja i wykonanie JIT.", "MemoryManagerUnsafeTooltip": "Bezpośrednio mapuj pamięć, ale nie maskuj adresu w przestrzeni adresowej gościa przed uzyskaniem dostępu. Szybciej, ale kosztem bezpieczeństwa. Aplikacja gościa może uzyskać dostęp do pamięci z dowolnego miejsca w Ryujinx, więc w tym trybie uruchamiaj tylko programy, którym ufasz.", "UseHypervisorTooltip": "Użyj Hiperwizora zamiast JIT. Znacznie poprawia wydajność, gdy jest dostępny, ale może być niestabilny w swoim obecnym stanie ", - "DRamTooltip": "Wykorzystuje alternatywny układ MemoryMode, aby naśladować model rozwojowy Switcha.\n\nJest to przydatne tylko w przypadku pakietów tekstur o wyższej rozdzielczości lub modów w rozdzielczości 4k. NIE poprawia wydajności.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignoruje niezaimplementowane usługi Horizon OS. Może to pomóc w ominięciu awarii podczas uruchamiania niektórych gier.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", "GraphicsBackendThreadingTooltip": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", "GalThreadingTooltip": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", @@ -620,12 +622,12 @@ "ControllerSettingsStickRange": "Zasięg:", "DialogStopEmulationTitle": "Ryujinx - Zatrzymaj Emulację", "DialogStopEmulationMessage": "Czy na pewno chcesz zatrzymać emulację?", - "SettingsTabCpu": "CPU", + "SettingsTabCpu": "Procesor", "SettingsTabAudio": "Dżwięk", "SettingsTabNetwork": "Sieć", "SettingsTabNetworkConnection": "Połączenie Sieciowe", - "SettingsTabCpuCache": "Cache CPU", - "SettingsTabCpuMemory": "Pamięć CPU", + "SettingsTabCpuCache": "Pamięć podręczna procesora", + "SettingsTabCpuMemory": "Tryb procesora", "DialogUpdaterFlatpakNotSupportedMessage": "Zaktualizuj Ryujinx przez FlatHub.", "UpdaterDisabledWarningTitle": "Aktualizator Wyłączony!", "ControllerSettingsRotate90": "Obróć o 90° w Prawo", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Otwórz Podręcznik Konfiguracji", "NoUpdate": "Brak Aktualizacji", "TitleUpdateVersionLabel": "Wersja {0} - {1}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Potwierdzenie", "FileDialogAllTypes": "Wszystkie typy", @@ -681,8 +685,8 @@ "OpenFolderDialogTitle": "Wybierz folder z rozpakowaną grą", "AllSupportedFormats": "Wszystkie Obsługiwane Formaty", "RyujinxUpdater": "Aktualizator Ryujinx", - "SettingsTabHotkeys": "Skróty Klawiszowe Klawiatury", - "SettingsTabHotkeysHotkeys": "Skróty Klawiszowe Klawiatury", + "SettingsTabHotkeys": "Skróty klawiszowe", + "SettingsTabHotkeysHotkeys": "Skróty klawiszowe", "SettingsTabHotkeysToggleVsyncHotkey": "Przełącz VSync:", "SettingsTabHotkeysScreenshotHotkey": "Zrzut Ekranu:", "SettingsTabHotkeysShowUiHotkey": "Pokaż UI:", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAALabel": "Antyaliasing:", "GraphicsScalingFilterLabel": "Filtr skalowania:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Dwuliniowe", "GraphicsScalingFilterNearest": "Najbliższe", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Poziom", "GraphicsScalingFilterLevelTooltip": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.", "SmaaLow": "SMAA Niskie", diff --git a/src/Ryujinx/Assets/Locales/pt_BR.json b/src/Ryujinx/Assets/Locales/pt_BR.json index a8c244b65..71d0e184e 100644 --- a/src/Ryujinx/Assets/Locales/pt_BR.json +++ b/src/Ryujinx/Assets/Locales/pt_BR.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Usar Hipervisor", "MenuBarFile": "_Arquivo", "MenuBarFileOpenFromFile": "_Abrir ROM do jogo...", + "MenuBarFileOpenFromFileError": "Nenhuma aplicação encontrada no arquivo selecionado.", "MenuBarFileOpenUnpacked": "Abrir jogo _extraído...", "MenuBarFileOpenEmuFolder": "Abrir diretório do e_mulador...", "MenuBarFileOpenLogsFolder": "Abrir diretório de _logs...", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Gerenciar tipos de arquivo", "MenuBarToolsInstallFileTypes": "Instalar tipos de arquivo", "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de arquivos", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Visualizar", + "MenuBarViewWindow": "Tamanho da Janela", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Ajuda", @@ -80,7 +81,7 @@ "GameListContextMenuOpenModsDirectory": "Abrir pasta de Mods", "GameListContextMenuOpenModsDirectoryToolTip": "Abre a pasta que contém os mods da aplicação ", "GameListContextMenuOpenSdModsDirectory": "Abrir diretório de mods Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Abre o diretório Atmosphere do cartão SD alternativo que contém os Mods do aplicativo. Útil para mods que são empacotados para hardware real.", "StatusBarGamesLoaded": "{0}/{1} jogos carregados", "StatusBarSystemVersion": "Versão do firmware: {0}", "LinuxVmMaxMapCountDialogTitle": "Limite baixo para mapeamentos de memória detectado", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Pode causar instabilidade)", - "SettingsTabSystemExpandDramSize": "Expandir memória para 6GiB", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorar serviços não implementados", "SettingsTabGraphics": "Gráficos", "SettingsTabGraphicsAPI": "API gráfica", @@ -271,29 +272,29 @@ "ControllerSettingsMotionGyroDeadzone": "Zona morta do giroscópio:", "ControllerSettingsSave": "Salvar", "ControllerSettingsClose": "Fechar", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", + "KeyUnknown": "Desconhecido", + "KeyShiftLeft": "Shift Esquerdo", + "KeyShiftRight": "Shift Direito", + "KeyControlLeft": "Ctrl Esquerdo", + "KeyMacControlLeft": "^ Esquerdo", + "KeyControlRight": "Ctrl Direito", + "KeyMacControlRight": "^ Direito", + "KeyAltLeft": "Alt Esquerdo", + "KeyMacAltLeft": "⌥ Esquerdo", + "KeyAltRight": "Alt Direito", + "KeyMacAltRight": "⌥ Direito", + "KeyWinLeft": "⊞ Esquerdo", + "KeyMacWinLeft": "⌘ Esquerdo", + "KeyWinRight": "⊞ Direito", + "KeyMacWinRight": "⌘ Direito", "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", + "KeyUp": "Cima", + "KeyDown": "Baixo", + "KeyLeft": "Esquerda", + "KeyRight": "Direita", + "KeyEnter": "Entrar", "KeyEscape": "Escape", - "KeySpace": "Space", + "KeySpace": "Espaço", "KeyTab": "Tab", "KeyBackSpace": "Backspace", "KeyInsert": "Insert", @@ -304,24 +305,24 @@ "KeyEnd": "End", "KeyCapsLock": "Caps Lock", "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", + "KeyPrintScreen": "Captura de ecrã", + "KeyPause": "Pausar", "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", + "KeyClear": "Limpar", + "KeyKeypad0": "Teclado Numérico 0", + "KeyKeypad1": "Teclado Numérico 1", + "KeyKeypad2": "Teclado Numérico 2", + "KeyKeypad3": "Teclado Numérico 3", + "KeyKeypad4": "Teclado Numérico 4", + "KeyKeypad5": "Teclado Numérico 5", + "KeyKeypad6": "Teclado Numérico 6", + "KeyKeypad7": "Teclado Numérico 7", + "KeyKeypad8": "Teclado Numérico 8", + "KeyKeypad9": "Teclado Numérico 9", + "KeyKeypadDivide": "Keypad Dividir", + "KeyKeypadMultiply": "Keypad Multiplicar", + "KeyKeypadSubtract": "Keypad Subtrair", + "KeyKeypadAdd": "Keypad Adicionar", "KeyKeypadDecimal": "Keypad Decimal", "KeyKeypadEnter": "Keypad Enter", "KeyNumber0": "0", @@ -346,32 +347,32 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "KeyUnbound": "Desvincular", + "GamepadLeftStick": "Botão Analógico Esquerdo", + "GamepadRightStick": "Botão Analógico Direito", + "GamepadLeftShoulder": "Botão superior esquerdo", + "GamepadRightShoulder": "Botão superior direito", + "GamepadLeftTrigger": "Botão LT", + "GamepadRightTrigger": "Botão RT", + "GamepadDpadUp": "Cima", + "GamepadDpadDown": "Baixo", + "GamepadDpadLeft": "Esquerda", + "GamepadDpadRight": "Direita", "GamepadMinus": "-", "GamepadPlus": "+", - "GamepadGuide": "Guide", + "GamepadGuide": "Guia", "GamepadMisc1": "Misc", "GamepadPaddle1": "Paddle 1", "GamepadPaddle2": "Paddle 2", "GamepadPaddle3": "Paddle 3", "GamepadPaddle4": "Paddle 4", "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "GamepadSingleLeftTrigger0": "Botão LT 0", + "GamepadSingleRightTrigger0": "Botão RT 0", + "GamepadSingleLeftTrigger1": "Botão LT 1", + "GamepadSingleRightTrigger1": "Botão RT 1", + "StickLeft": "Analógico esquerdo", + "StickRight": "Analógico direito", "UserProfilesSelectedUserProfile": "Perfil de usuário selecionado:", "UserProfilesSaveProfileName": "Salvar nome de perfil", "UserProfilesChangeProfileImage": "Mudar imagem de perfil", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Alternar favorito", "GameListContextMenuToggleFavoriteToolTip": "Marca ou desmarca jogo como favorito", "SettingsTabGeneralTheme": "Tema:", + "SettingsTabGeneralThemeAuto": "Auto", "SettingsTabGeneralThemeDark": "Escuro", "SettingsTabGeneralThemeLight": "Claro", "ControllerSettingsConfigureGeneral": "Configurar", @@ -447,7 +449,7 @@ "DialogThemeRestartMessage": "O tema foi salvo. Uma reinicialização é necessária para aplicar o tema.", "DialogThemeRestartSubMessage": "Deseja reiniciar?", "DialogFirmwareInstallEmbeddedMessage": "Gostaria de instalar o firmware incluso neste jogo? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", + "DialogFirmwareInstallEmbeddedSuccessMessage": "Nenhum firmware instalado foi encontrado, mas Ryujinx conseguiu instalar o firmware {0} do jogo fornecido.\nO emulador será iniciado agora.", "DialogFirmwareNoFirmwareInstalledMessage": "Firmware não foi instalado", "DialogFirmwareInstalledMessage": "Firmware {0} foi instalado", "DialogInstallFileTypesSuccessMessage": "Tipos de arquivo instalados com sucesso!", @@ -490,10 +492,10 @@ "DialogUserProfileUnsavedChangesSubMessage": "Deseja descartar as alterações?", "DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.", "DialogControllerSettingsModifiedConfirmSubMessage": "Deseja salvar?", - "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", - "DialogModAlreadyExistsMessage": "Mod already exists", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", - "DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!", + "DialogLoadFileErrorMessage": "{0}. Ficheiro com erro: {1}", + "DialogModAlreadyExistsMessage": "Mod já existente", + "DialogModInvalidMessage": "O diretório especificado não contém um mod!", + "DialogModDeleteNoParentMessage": "Falha ao apagar: Não foi possível encontrar o diretório pai para o mod \"{0}\"!", "DialogDlcNoDlcErrorMessage": "O arquivo especificado não contém DLCs para o título selecionado!", "DialogPerformanceCheckLoggingEnabledMessage": "Os logs de depuração estão ativos, esse recurso é feito para ser usado apenas por desenvolvedores.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar os logs de depuração. Gostaria de desabilitar os logs de depuração agora?", @@ -504,8 +506,8 @@ "DialogUpdateAddUpdateErrorMessage": "O arquivo especificado não contém atualizações para o título selecionado!", "DialogSettingsBackendThreadingWarningTitle": "Alerta - Threading da API gráfica", "DialogSettingsBackendThreadingWarningMessage": "Ryujinx precisa ser reiniciado após mudar essa opção para que ela tenha efeito. Dependendo da sua plataforma, pode ser preciso desabilitar o multithreading do driver de vídeo quando usar o Ryujinx.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", + "DialogModManagerDeletionWarningMessage": "Está prestes a apagar o mod: {0}\n\nTem a certeza que deseja prosseguir?", + "DialogModManagerDeletionAllWarningMessage": "Está prestes a apagar todos os mods para este título.\n\nTem a certeza que deseja prosseguir?", "SettingsTabGraphicsFeaturesOptions": "Recursos", "SettingsTabGraphicsBackendMultithreading": "Multithreading da API gráfica:", "CommonAuto": "Automático", @@ -540,7 +542,7 @@ "DlcManagerRemoveAllButton": "Remover todos", "DlcManagerEnableAllButton": "Habilitar todos", "DlcManagerDisableAllButton": "Desabilitar todos", - "ModManagerDeleteAllButton": "Delete All", + "ModManagerDeleteAllButton": "Apagar todos", "MenuBarOptionsChangeLanguage": "Mudar idioma", "MenuBarShowFileTypes": "Mostrar tipos de arquivo", "CommonSort": "Ordenar", @@ -558,13 +560,13 @@ "CustomThemePathTooltip": "Diretório do tema customizado", "CustomThemeBrowseTooltip": "Navegar até um tema customizado", "DockModeToggleTooltip": "Habilita ou desabilita modo TV", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", + "DirectKeyboardTooltip": "Suporte direto ao acesso do teclado (HID). Permite que os jogos acessem seu teclado como um dispositivo de entrada de texto.\n\nFunciona apenas com jogos que suportam nativamente o uso do teclado no hardware do Switch.\n\nDeixe DESATIVADO se estiver em dúvida.", + "DirectMouseTooltip": "Suporte direto ao acesso do mouse (HID). Permite que os jogos acessem seu mouse como um dispositivo apontador.\n\nFunciona apenas com jogos que suportam nativamente controles de mouse no hardware do Switch, o que é raro.\n\nQuando ativado, a funcionalidade de tela sensível ao toque pode não funcionar.\n\nDeixe DESATIVADO se estiver em dúvida.", "RegionTooltip": "Mudar a região do sistema", "LanguageTooltip": "Mudar o idioma do sistema", "TimezoneTooltip": "Mudar o fuso-horário do sistema", "TimeTooltip": "Mudar a hora do sistema", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", + "VSyncToggleTooltip": "Sincronização Vertical do Console Emulado. Essencialmente, um limitador de quadros para a maioria dos jogos; desativá-lo pode fazer com que os jogos funcionem em velocidades mais altas, causar lentidão ou travamentos nas telas de carregamento.\n\nPode ser alternado durante o jogo com uma tecla de atalho de sua preferência (por padrão, F1). Recomendamos fazer isso se você planeja desativá-lo.\n\nDeixe ATIVADO se estiver em dúvida.", "PptcToggleTooltip": "Habilita ou desabilita PPTC", "FsIntegrityToggleTooltip": "Habilita ou desabilita verificação de integridade dos arquivos do jogo", "AudioBackendTooltip": "Mudar biblioteca de áudio", @@ -573,15 +575,15 @@ "MemoryManagerHostTooltip": "Mapeia memória no espaço de endereço hóspede diretamente. Compilação e execução do JIT muito mais rápida.", "MemoryManagerUnsafeTooltip": "Mapeia memória diretamente, mas sem limitar o acesso ao espaço de endereçamento do sistema convidado. Mais rápido, porém menos seguro. O aplicativo convidado pode acessar memória de qualquer parte do Ryujinx, então apenas rode programas em que você confia nesse modo.", "UseHypervisorTooltip": "Usa o Hypervisor em vez de JIT (recompilador dinâmico). Melhora significativamente o desempenho quando disponível, mas pode ser instável no seu estado atual.", - "DRamTooltip": "Expande a memória do sistema emulado de 4GiB para 6GiB", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Habilita ou desabilita a opção de ignorar serviços não implementados", "GraphicsBackendThreadingTooltip": "Habilita multithreading do backend gráfico", "GalThreadingTooltip": "Executa comandos do backend gráfico em uma segunda thread. Permite multithreading em tempo de execução da compilação de shader, diminui os travamentos, e melhora performance em drivers sem suporte embutido a multithreading. Pequena variação na performance máxima em drivers com suporte a multithreading. Ryujinx pode precisar ser reiniciado para desabilitar adequadamente o multithreading embutido do driver, ou você pode precisar fazer isso manualmente para ter a melhor performance.", "ShaderCacheToggleTooltip": "Habilita ou desabilita o cache de shader", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", + "ResolutionScaleTooltip": "Multiplica a resolução de renderização do jogo.\n\nAlguns jogos podem não funcionar com isso e parecer pixelizados mesmo quando a resolução é aumentada; para esses jogos, pode ser necessário encontrar mods que removam o anti-aliasing ou que aumentem a resolução de renderização interna. Para usar este último, provavelmente você desejará selecionar \"Nativa\".\n\nEsta opção pode ser alterada enquanto um jogo está em execução clicando em \"Aplicar\" abaixo; você pode simplesmente mover a janela de configurações para o lado e experimentar até encontrar a aparência preferida para um jogo.\n\nLembre-se de que 4x é exagerado para praticamente qualquer configuração.", "ResolutionScaleEntryTooltip": "Escala de resolução de ponto flutuante, como 1.5. Valores não inteiros tem probabilidade maior de causar problemas ou quebras.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", + "AnisotropyTooltip": "Nível de filtragem anisotrópica. Defina como Auto para usar o valor solicitado pelo jogo.", + "AspectRatioTooltip": "Proporção de aspecto aplicada à janela do renderizador.\n\nApenas altere este ratio se estiver usando um mod de proporção de aspecto para o jogo; caso contrário, os gráficos serão esticados.\n\nDeixe em 16:9 se não tiver a certeza.", "ShaderDumpPathTooltip": "Diretòrio de despejo de shaders", "FileLogTooltip": "Habilita ou desabilita log para um arquivo no disco", "StubLogTooltip": "Habilita ou desabilita exibição de mensagens de stub", @@ -615,8 +617,8 @@ "EnableInternetAccessTooltip": "Habilita acesso à internet do programa convidado. Se habilitado, o aplicativo vai se comportar como se o sistema Switch emulado estivesse conectado a Internet. Note que em alguns casos, aplicativos podem acessar a Internet mesmo com essa opção desabilitada", "GameListContextMenuManageCheatToolTip": "Gerenciar Cheats", "GameListContextMenuManageCheat": "Gerenciar Cheats", - "GameListContextMenuManageModToolTip": "Manage Mods", - "GameListContextMenuManageMod": "Manage Mods", + "GameListContextMenuManageModToolTip": "Gerir mods", + "GameListContextMenuManageMod": "Gerir mods", "ControllerSettingsStickRange": "Intervalo:", "DialogStopEmulationTitle": "Ryujinx - Parar emulação", "DialogStopEmulationMessage": "Tem certeza que deseja parar a emulação?", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Abrir o guia de configuração", "NoUpdate": "Sem atualizações", "TitleUpdateVersionLabel": "Versão {0} - {1}", + "TitleBundledUpdateVersionLabel": "Embutido: Versão {0}", + "TitleBundledDlcLabel": "Embutido:", "RyujinxInfo": "Ryujinx - Informação", "RyujinxConfirm": "Ryujinx - Confirmação", "FileDialogAllTypes": "Todos os tipos", @@ -658,10 +662,10 @@ "SoftwareKeyboardModeNumeric": "Deve ser somente 0-9 ou '.'", "SoftwareKeyboardModeAlphabet": "Apenas devem ser caracteres não CJK.", "SoftwareKeyboardModeASCII": "Deve ser apenas texto ASCII", - "ControllerAppletControllers": "Supported Controllers:", - "ControllerAppletPlayers": "Players:", - "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.", - "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", + "ControllerAppletControllers": "Controladores suportados:", + "ControllerAppletPlayers": "Jogadores:", + "ControllerAppletDescription": "Sua configuração atual é inválida. Abra as configurações e reconfigure suas entradas.", + "ControllerAppletDocked": "Modo acoplado definido. O controle do computador de mão deve ser desativado.", "UpdaterRenaming": "Renomeando arquivos antigos...", "UpdaterRenameFailed": "O atualizador não conseguiu renomear o arquivo: {0}", "UpdaterAddingFiles": "Adicionando novos arquivos...", @@ -699,11 +703,11 @@ "Writable": "Gravável", "SelectDlcDialogTitle": "Selecionar arquivos de DLC", "SelectUpdateDialogTitle": "Selecionar arquivos de atualização", - "SelectModDialogTitle": "Select mod directory", + "SelectModDialogTitle": "Selecione o diretório dos mods", "UserProfileWindowTitle": "Gerenciador de perfis de usuário", "CheatWindowTitle": "Gerenciador de Cheats", "DlcWindowTitle": "Gerenciador de DLC", - "ModWindowTitle": "Manage Mods for {0} ({1})", + "ModWindowTitle": "Gerir mods para {0} ({1})", "UpdateWindowTitle": "Gerenciador de atualizações", "CheatWindowHeading": "Cheats disponíveis para {0} [{1}]", "BuildId": "ID da Build", @@ -713,7 +717,7 @@ "Cancel": "Cancelar", "Save": "Salvar", "Discard": "Descartar", - "Paused": "Paused", + "Paused": "Pausado", "UserProfilesSetProfileImage": "Definir imagem de perfil", "UserProfileEmptyNameError": "É necessário um nome", "UserProfileNoImageError": "A imagem de perfil deve ser definida", @@ -723,9 +727,9 @@ "UserProfilesName": "Nome:", "UserProfilesUserId": "ID de usuário:", "SettingsTabGraphicsBackend": "Backend gráfico", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", + "SettingsTabGraphicsBackendTooltip": "Selecione o backend gráfico que será usado no emulador.\n\nVulkan é geralmente melhor para todas as placas gráficas modernas, contanto que seus drivers estejam atualizados. Vulkan também apresenta uma compilação de shader mais rápida (menos travamentos) em todos os fornecedores de GPU.\n\nOpenGL pode alcançar melhores resultados em GPUs Nvidia antigas, em GPUs AMD antigas no Linux, ou em GPUs com menor VRAM, embora os travamentos na compilação de shaders sejam maiores.\n\nDefina como Vulkan se estiver em dúvida. Defina como OpenGL se sua GPU não suportar Vulkan mesmo com os drivers gráficos mais recentes.", "SettingsEnableTextureRecompression": "Habilitar recompressão de texturas", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", + "SettingsEnableTextureRecompressionTooltip": "Comprime texturas ASTC para reduzir o uso de VRAM.\n\nJogos que utilizam este formato de textura incluem Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder e The Legend of Zelda: Tears of the Kingdom.\n\nPlacas gráficas com 4GiB de VRAM ou menos provavelmente irão travar em algum momento ao executar esses jogos.\n\nAtive apenas se estiver com pouco VRAM nos jogos mencionados. Deixe DESATIVADO se estiver em dúvida.", "SettingsTabGraphicsPreferredGpu": "GPU preferencial", "SettingsTabGraphicsPreferredGpuTooltip": "Selecione a placa de vídeo que será usada com o backend gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.", "SettingsAppRequiredRestartMessage": "Reinicialização do Ryujinx necessária", @@ -751,15 +755,16 @@ "Recover": "Recuperar", "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas", "UserProfilesRecoverEmptyList": "Nenhum perfil para recuperar", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", + "GraphicsAATooltip": "Aplica anti-aliasing à renderização do jogo.\n\nFXAA irá desfocar a maioria da imagem, enquanto SMAA tentará identificar bordas serrilhadas e suavizá-las.\n\nNão é recomendado usar em conjunto com o filtro de escala FSR.\n\nEsta opção pode ser alterada enquanto um jogo está em execução clicando em \"Aplicar\" abaixo; você pode simplesmente mover a janela de configurações para o lado e experimentar até encontrar a aparência preferida para um jogo.\n\nDeixe como NENHUM se estiver em dúvida.", "GraphicsAALabel": "Anti-serrilhado:", "GraphicsScalingFilterLabel": "Filtro de escala:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Nível", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", + "GraphicsScalingFilterLevelTooltip": "Defina o nível de nitidez do FSR 1.0. Mais alto é mais nítido.", "SmaaLow": "SMAA Baixo", "SmaaMedium": "SMAA Médio", "SmaaHigh": "SMAA Alto", @@ -767,14 +772,14 @@ "UserEditorTitle": "Editar usuário", "UserEditorTitleCreate": "Criar usuário", "SettingsTabNetworkInterface": "Interface de rede:", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", + "NetworkInterfaceTooltip": "A interface de rede usada para recursos LAN/LDN.\n\nEm conjunto com uma VPN ou XLink Kai e um jogo com suporte a LAN, pode ser usada para simular uma conexão na mesma rede pela Internet.\n\nDeixe como PADRÃO se estiver em dúvida.", "NetworkInterfaceDefault": "Padrão", "PackagingShaders": "Empacotamento de Shaders", "AboutChangelogButton": "Ver mudanças no GitHub", "AboutChangelogButtonTooltipMessage": "Clique para abrir o relatório de alterações para esta versão no seu navegador padrão.", "SettingsTabNetworkMultiplayer": "Multiplayer", "MultiplayerMode": "Modo:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" + "MultiplayerModeTooltip": "Alterar Modo Multiplayer com Rede LDN.\n\nO LdnMitm modificará a funcionalidade de jogo wireless/local em jogos para funcionar como se fosse uma LAN, permitindo conexões locais na mesma rede com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que têm o módulo ldn_mitm instalado.\n\nO Modo Multiplayer exige que todos os jogadores estejam na mesma versão do jogo (por exemplo, Super Smash Bros. Ultimate v13.0.1 não pode se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.", + "MultiplayerModeDisabled": "Desabilitado", + "MultiplayerModeLdnMitm": "Modo Multiplayer com Rede LDN" } diff --git a/src/Ryujinx/Assets/Locales/ru_RU.json b/src/Ryujinx/Assets/Locales/ru_RU.json index 75fd4fe12..1305990c5 100644 --- a/src/Ryujinx/Assets/Locales/ru_RU.json +++ b/src/Ryujinx/Assets/Locales/ru_RU.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Использовать Hypervisor", "MenuBarFile": "_Файл", "MenuBarFileOpenFromFile": "_Добавить приложение из файла", + "MenuBarFileOpenFromFileError": "В выбранном файле игра не найдена.", "MenuBarFileOpenUnpacked": "Добавить _распакованную игру", "MenuBarFileOpenEmuFolder": "Открыть папку Ryujinx", "MenuBarFileOpenLogsFolder": "Открыть папку с логами", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Хаки", "SettingsTabSystemHacksNote": "Возможна нестабильная работа", - "SettingsTabSystemExpandDramSize": "Использовать альтернативный макет памяти (для разработчиков)", + "SettingsTabSystemExpandDramSize": "Расширение DRAM до 8ГБ", "SettingsTabSystemIgnoreMissingServices": "Игнорировать отсутствующие службы", "SettingsTabGraphics": "Графика", "SettingsTabGraphicsAPI": "Графические API", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Добавить в избранное", "GameListContextMenuToggleFavoriteToolTip": "Добавляет игру в избранное и помечает звездочкой", "SettingsTabGeneralTheme": "Тема:", + "SettingsTabGeneralThemeAuto": "Системная", "SettingsTabGeneralThemeDark": "Темная", "SettingsTabGeneralThemeLight": "Светлая", "ControllerSettingsConfigureGeneral": "Настройка", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "Прямая разметка памяти в адресном пространстве хоста. \nЗначительно более быстрые запуск и компиляция JIT.", "MemoryManagerUnsafeTooltip": "Производит прямую разметку памяти, но не маскирует адрес в гостевом адресном пространстве перед получением доступа. \nБыстро, но небезопасно. Гостевое приложение может получить доступ к памяти из Ryujinx, поэтому в этом режиме рекомендуется запускать только те программы, которым вы доверяете.", "UseHypervisorTooltip": "Использует Hypervisor вместо JIT. Значительно увеличивает производительность, но может работать нестабильно.", - "DRamTooltip": "Использует альтернативный макет MemoryMode для имитации использования Nintendo Switch в режиме разработчика.\n\nПолезно только для пакетов текстур с высоким разрешением или модов добавляющих разрешение 4К. Не улучшает производительность.\n\nРекомендуется оставить выключенным.", + "DRamTooltip": "Использует альтернативный режим памяти в 8ГБ DRAM, для имитации модели Switch для разработчиков.\n\nПолезно только для пакетов текстур с высоким разрешением или модификаций с разрешением 4K. Не улучшает производительность.\n\nНе включайте без необходимости.", "IgnoreMissingServicesTooltip": "Игнорирует нереализованные сервисы Horizon в новых прошивках. Эта настройка поможет избежать вылеты при запуске определенных игр.\n\nРекомендуется оставить выключенным.", "GraphicsBackendThreadingTooltip": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", "GalThreadingTooltip": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", @@ -647,7 +649,9 @@ "UserErrorUndefinedDescription": "Произошла неизвестная ошибка. Этого не должно происходить, пожалуйста, свяжитесь с разработчиками.", "OpenSetupGuideMessage": "Открыть руководство по установке", "NoUpdate": "Без обновлений", - "TitleUpdateVersionLabel": "Version {0} - {1}", + "TitleUpdateVersionLabel": "Версия {0}", + "TitleBundledUpdateVersionLabel": "Комплект: Версия {0}", + "TitleBundledDlcLabel": "Комплект:", "RyujinxInfo": "Ryujinx - Информация", "RyujinxConfirm": "Ryujinx - Подтверждение", "FileDialogAllTypes": "Все типы", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти \"зазубренные\" края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать \"Нет\".", "GraphicsAALabel": "Сглаживание:", "GraphicsScalingFilterLabel": "Интерполяция:", - "GraphicsScalingFilterTooltip": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать \"Билинейная\".", + "GraphicsScalingFilterTooltip": "Фильтрация текстур, которая будет применяться для масштабирования разрешения.\n\nБилинейная хорошо работает для 3D-игр и установлена по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR — это фильтр резкости, не рекомендуется использовать вместе с FXAA или SMAA.\n\nМаштабирование по площади рекомендуется в случае использования разрешения больше разрешения окна. Можно использовать для достижения эффекта суперсемплига (SSAA) при даунскейле более чем в 2 раза.\n\nЭта опция может быть применена во время игры по нажатию кнопки «Применить» ниже; Вы можете передвинуть окно настроек в сторону и экспериментировать, пока не найдете подходящие вам настройки для конкретной игры.\n\nРекомендуется использовать «Билинейная».", "GraphicsScalingFilterBilinear": "Билинейная", "GraphicsScalingFilterNearest": "Ступенчатая", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "По площади", "GraphicsScalingFilterLevelLabel": "Уровень", "GraphicsScalingFilterLevelTooltip": "Выбор режима работы FSR 1.0. Выше - четче.", "SmaaLow": "SMAA Низкое", diff --git a/src/Ryujinx/Assets/Locales/th_TH.json b/src/Ryujinx/Assets/Locales/th_TH.json index 629442269..6fb35aa51 100644 --- a/src/Ryujinx/Assets/Locales/th_TH.json +++ b/src/Ryujinx/Assets/Locales/th_TH.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "ใช้งาน Hypervisor", "MenuBarFile": "ไฟล์", "MenuBarFileOpenFromFile": "โหลดแอปพลิเคชั่นจากไฟล์", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "โหลดเกมที่คลายแพ็กแล้ว", "MenuBarFileOpenEmuFolder": "เปิดโฟลเดอร์ Ryujinx", "MenuBarFileOpenLogsFolder": "เปิดโฟลเดอร์ Logs", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "จัดการประเภทไฟล์", "MenuBarToolsInstallFileTypes": "ติดตั้งตามประเภทของไฟล์", "MenuBarToolsUninstallFileTypes": "ถอนการติดตั้งตามประเภทของไฟล์", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "มุมมอง", + "MenuBarViewWindow": "ขนาดหน้าต่าง", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_ช่วยเหลือ", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "เปิดใช้งาน Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "ตรวจหาการอัปเดตเมื่อเปิดโปรแกรม", "SettingsTabGeneralShowConfirmExitDialog": "แสดง \"ยืนยันการออก\" กล่องข้อความโต้ตอบ", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "จดจำขนาด / ตำแหน่งของหน้าต่าง", "SettingsTabGeneralHideCursor": "ซ่อน เคอร์เซอร์:", "SettingsTabGeneralHideCursorNever": "ไม่มี", "SettingsTabGeneralHideCursorOnIdle": "เมื่อไม่ได้ใช้", @@ -134,7 +135,7 @@ "SettingsTabSystemSystemLanguageTraditionalChinese": "จีน (ดั้งเดิม)", "SettingsTabSystemSystemTimeZone": "เขตเวลาของระบบ:", "SettingsTabSystemSystemTime": "เวลาของระบบ:", - "SettingsTabSystemEnableVsync": "VSync", + "SettingsTabSystemEnableVsync": "วีซิงค์ ", "SettingsTabSystemEnablePptc": "PPTC (แคชโปรไฟล์การแปลแบบถาวร)", "SettingsTabSystemEnableFsIntegrityChecks": "ตรวจสอบความถูกต้องของ FS", "SettingsTabSystemAudioBackend": "ระบบเสียงเบื้องหลัง:", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "แฮ็ก", "SettingsTabSystemHacksNote": "อาจทำให้เกิดข้อผิดพลาดได้", - "SettingsTabSystemExpandDramSize": "ใช้รูปแบบหน่วยความจำสำรอง (โหมดนักพัฒนา)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "ไม่สนใจบริการที่ขาดหายไป", "SettingsTabGraphics": "กราฟิก", "SettingsTabGraphicsAPI": "กราฟฟิก API", @@ -271,26 +272,26 @@ "ControllerSettingsMotionGyroDeadzone": "ส่วนไม่ทำงานของไจโร:", "ControllerSettingsSave": "บันทึก", "ControllerSettingsClose": "ปิด", - "KeyUnknown": "Unknown", + "KeyUnknown": "ไม่รู้จัก", "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", + "KeyShiftRight": "Shift Left", "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", + "KeyMacControlLeft": "⌃ ซ้าย", "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", + "KeyMacControlRight": "⌃ ขวา", "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", + "KeyMacAltLeft": "⌥ ซ้าย", "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyMacAltRight": "⌥ ขวา", + "KeyWinLeft": "⊞ ซ้าย", + "KeyMacWinLeft": "⌘ ซ้าย", + "KeyWinRight": "⊞ ขวา", + "KeyMacWinRight": "⌘ ขวา", + "KeyMenu": "เมนู", + "KeyUp": "ขึ้น", + "KeyDown": "ลง", + "KeyLeft": "ซ้าย", + "KeyRight": "ขวา", "KeyEnter": "Enter", "KeyEscape": "Escape", "KeySpace": "Space", @@ -307,7 +308,7 @@ "KeyPrintScreen": "Print Screen", "KeyPause": "Pause", "KeyNumLock": "Num Lock", - "KeyClear": "Clear", + "KeyClear": "ล้าง", "KeyKeypad0": "Keypad 0", "KeyKeypad1": "Keypad 1", "KeyKeypad2": "Keypad 2", @@ -321,7 +322,7 @@ "KeyKeypadDivide": "Keypad Divide", "KeyKeypadMultiply": "Keypad Multiply", "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", + "KeyKeypadAdd": "เพิ่มปุ่มกด", "KeyKeypadDecimal": "Keypad Decimal", "KeyKeypadEnter": "Keypad Enter", "KeyNumber0": "0", @@ -340,38 +341,38 @@ "KeyPlus": "+", "KeyBracketLeft": "[", "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", + "KeySemicolon": "`", + "KeyQuote": "`", + "KeyComma": "`", "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", + "KeyUnbound": "ยังไม่ผูกติด", "GamepadLeftStick": "L Stick Button", "GamepadRightStick": "R Stick Button", "GamepadLeftShoulder": "Left Shoulder", "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "GamepadLeftTrigger": "Trigger ซ้าย", + "GamepadRightTrigger": "Trigger ขวา", + "GamepadDpadUp": "ขึ้น", + "GamepadDpadDown": "ลง", + "GamepadDpadLeft": "ซ้าย", + "GamepadDpadRight": "ขวา", "GamepadMinus": "-", "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", + "GamepadGuide": "คู่มือ", + "GamepadMisc1": "อื่นๆ", "GamepadPaddle1": "Paddle 1", "GamepadPaddle2": "Paddle 2", "GamepadPaddle3": "Paddle 3", "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", + "GamepadTouchpad": "ทัชแพด", "GamepadSingleLeftTrigger0": "Left Trigger 0", "GamepadSingleRightTrigger0": "Right Trigger 0", "GamepadSingleLeftTrigger1": "Left Trigger 1", "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "StickLeft": "สติ๊กซ้าย", + "StickRight": "สติ๊กขวา", "UserProfilesSelectedUserProfile": "โปรไฟล์ผู้ใช้งานที่เลือก:", "UserProfilesSaveProfileName": "บันทึกชื่อโปรไฟล์", "UserProfilesChangeProfileImage": "เปลี่ยนรูปโปรไฟล์", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "สลับรายการโปรด", "GameListContextMenuToggleFavoriteToolTip": "สลับสถานะเกมที่ชื่นชอบ", "SettingsTabGeneralTheme": "ธีม:", + "SettingsTabGeneralThemeAuto": "อัตโนมัติ", "SettingsTabGeneralThemeDark": "มืด", "SettingsTabGeneralThemeLight": "สว่าง", "ControllerSettingsConfigureGeneral": "กำหนดค่า", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "แมปหน่วยความจำในพื้นที่ที่อยู่โฮสต์โดยตรง การคอมไพล์และดำเนินการ JIT เร็วขึ้นมาก", "MemoryManagerUnsafeTooltip": "แมปหน่วยความจำโดยตรง แต่อย่าปิดบังที่อยู่ภายในพื้นที่ที่อยู่ของผู้เยี่ยมชมก่อนที่จะเข้าถึง เร็วกว่า แต่ต้องแลกกับความปลอดภัย แอปพลิเคชั่นผู้เยี่ยมชมสามารถเข้าถึงหน่วยความจำได้จากทุกที่ใน รียูจินซ์ ดังนั้นให้รันเฉพาะโปรแกรมที่คุณเชื่อถือในโหมดนี้", "UseHypervisorTooltip": "ใช้ Hypervisor แทน JIT ปรับปรุงประสิทธิภาพอย่างมากเมื่อพร้อมใช้งาน แต่อาจไม่เสถียรในสถานะปัจจุบัน", - "DRamTooltip": "ใช้เค้าโครง MemoryMode ทางเลือกเพื่อเลียนแบบโมเดลการพัฒนาสวิตช์\n\nสิ่งนี้มีประโยชน์สำหรับแพ็กพื้นผิวที่มีความละเอียดสูงกว่าหรือม็อดที่มีความละเอียด 4k เท่านั้น ไม่ปรับปรุงประสิทธิภาพ\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "ละเว้นบริการ Horizon OS ที่ยังไม่ได้ใช้งาน วิธีนี้อาจช่วยในการหลีกเลี่ยงข้อผิดพลาดเมื่อบู๊ตเกมบางเกม\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", "GraphicsBackendThreadingTooltip": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", "GalThreadingTooltip": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "เปิดคู่มือการตั้งค่า", "NoUpdate": "ไม่มีการอัปเดต", "TitleUpdateVersionLabel": "เวอร์ชั่น {0}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "รียูจินซ์ – ข้อมูล", "RyujinxConfirm": "รียูจินซ์ - ยืนยัน", "FileDialogAllTypes": "ทุกประเภท", @@ -703,7 +707,7 @@ "UserProfileWindowTitle": "จัดการโปรไฟล์ผู้ใช้", "CheatWindowTitle": "จัดการสูตรโกง", "DlcWindowTitle": "จัดการเนื้อหาที่ดาวน์โหลดได้สำหรับ {0} ({1})", - "ModWindowTitle": "Manage Mods for {0} ({1})", + "ModWindowTitle": "จัดการ Mods สำหรับ {0} ({1})", "UpdateWindowTitle": "จัดการอัปเดตหัวข้อ", "CheatWindowHeading": "สูตรโกงมีให้สำหรับ {0} [{1}]", "BuildId": "รหัสบิวด์:", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหาขอบหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ", "GraphicsAALabel": "ลดการฉีกขาดของภาพ:", "GraphicsScalingFilterLabel": "ปรับขนาดตัวกรอง:", - "GraphicsScalingFilterTooltip": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterBilinear": "ไบลิเนียร์", + "GraphicsScalingFilterNearest": "ใกล้ที่สุด", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "ระดับ", "GraphicsScalingFilterLevelTooltip": "ตั้งค่าระดับความคมชัด FSR 1.0 สูงกว่าจะคมชัดกว่า", "SmaaLow": "SMAA ต่ำ", @@ -775,6 +780,6 @@ "SettingsTabNetworkMultiplayer": "ผู้เล่นหลายคน", "MultiplayerMode": "โหมด:", "MultiplayerModeTooltip": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ", - "MultiplayerModeDisabled": "Disabled", + "MultiplayerModeDisabled": "ปิดการใช้งาน", "MultiplayerModeLdnMitm": "ldn_mitm" } diff --git a/src/Ryujinx/Assets/Locales/tr_TR.json b/src/Ryujinx/Assets/Locales/tr_TR.json index f74baaa18..97ea5a2e9 100644 --- a/src/Ryujinx/Assets/Locales/tr_TR.json +++ b/src/Ryujinx/Assets/Locales/tr_TR.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Hypervisor Kullan", "MenuBarFile": "_Dosya", "MenuBarFileOpenFromFile": "_Dosyadan Uygulama Aç", + "MenuBarFileOpenFromFileError": "No applications found in selected file.", "MenuBarFileOpenUnpacked": "_Sıkıştırılmamış Oyun Aç", "MenuBarFileOpenEmuFolder": "Ryujinx Klasörünü aç", "MenuBarFileOpenLogsFolder": "Logs Klasörünü aç", @@ -47,13 +48,13 @@ "GameListHeaderLastPlayed": "Son Oynama Tarihi", "GameListHeaderFileExtension": "Dosya Uzantısı", "GameListHeaderFileSize": "Dosya Boyutu", - "GameListHeaderPath": "Yol", + "GameListHeaderPath": "Dosya Konumu", "GameListContextMenuOpenUserSaveDirectory": "Kullanıcı Kayıt Dosyası Dizinini Aç", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Uygulamanın Kullanıcı Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenDeviceSaveDirectory": "Kullanıcı Cihaz Dizinini Aç", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenBcatSaveDirectory": "Kullanıcı BCAT Dizinini Aç", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar", + "GameListContextMenuOpenDeviceSaveDirectory": "Cihaz Kayıt Dosyası Dizinini Aç", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Uygulamanın Cihaz Kayıt Dosyası'nın bulunduğu dizini açar", + "GameListContextMenuOpenBcatSaveDirectory": "BCAT Kayıt Dizinini Aç", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Uygulamanın BCAT Kaydı'nın bulunduğu dizini açar", "GameListContextMenuManageTitleUpdates": "Oyun Güncellemelerini Yönet", "GameListContextMenuManageTitleUpdatesToolTip": "Oyun Güncelleme Yönetim Penceresini Açar", "GameListContextMenuManageDlc": "DLC'leri Yönet", @@ -72,15 +73,15 @@ "GameListContextMenuExtractDataExeFSToolTip": "Uygulamanın geçerli yapılandırmasından ExeFS kısmını ayıkla (Güncellemeler dahil)", "GameListContextMenuExtractDataRomFS": "RomFS", "GameListContextMenuExtractDataRomFSToolTip": "Uygulamanın geçerli yapılandırmasından RomFS kısmını ayıkla (Güncellemeler dahil)", - "GameListContextMenuExtractDataLogo": "Simge", + "GameListContextMenuExtractDataLogo": "Logo", "GameListContextMenuExtractDataLogoToolTip": "Uygulamanın geçerli yapılandırmasından Logo kısmını ayıkla (Güncellemeler dahil)", "GameListContextMenuCreateShortcut": "Uygulama Kısayolu Oluştur", "GameListContextMenuCreateShortcutToolTip": "Seçilmiş uygulamayı çalıştıracak bir masaüstü kısayolu oluştur", - "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", + "GameListContextMenuCreateShortcutToolTipMacOS": "macOS'in Uygulamalar klasöründe seçili Uygulamayı başlatan bir kısayol oluştur", "GameListContextMenuOpenModsDirectory": "Mod Dizinini Aç", - "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", - "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", + "GameListContextMenuOpenModsDirectoryToolTip": "Uygulamanın modlarının bulunduğu dizini açar", + "GameListContextMenuOpenSdModsDirectory": "Atmosphere Mod dizinini aç", + "GameListContextMenuOpenSdModsDirectoryToolTip": "Uygulamanın modlarının bulunduğu alternatif SD Kart Atmosphere dizinini açar. Gerçek donanım için paketlenmiş modlar için kullanışlı olabilir", "StatusBarGamesLoaded": "{0}/{1} Oyun Yüklendi", "StatusBarSystemVersion": "Sistem Sürümü: {0}", "LinuxVmMaxMapCountDialogTitle": "Bellek Haritaları İçin Düşük Limit Tespit Edildi ", @@ -96,7 +97,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Discord Zengin İçerik'i Etkinleştir", "SettingsTabGeneralCheckUpdatesOnLaunch": "Her Açılışta Güncellemeleri Denetle", "SettingsTabGeneralShowConfirmExitDialog": "\"Çıkışı Onayla\" Diyaloğunu Göster", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralRememberWindowState": "Pencere yerini ve boyutunu hatırla", "SettingsTabGeneralHideCursor": "İşaretçiyi Gizle:", "SettingsTabGeneralHideCursorNever": "Hiçbir Zaman", "SettingsTabGeneralHideCursorOnIdle": "Hareketsiz Durumda", @@ -134,22 +135,22 @@ "SettingsTabSystemSystemLanguageTraditionalChinese": "Geleneksel Çince", "SettingsTabSystemSystemTimeZone": "Sistem Saat Dilimi:", "SettingsTabSystemSystemTime": "Sistem Saati:", - "SettingsTabSystemEnableVsync": "Dikey Eşitleme", + "SettingsTabSystemEnableVsync": "Dikey Senkronizasyon", "SettingsTabSystemEnablePptc": "PPTC (Profilli Sürekli Çeviri Önbelleği)", - "SettingsTabSystemEnableFsIntegrityChecks": "FS Bütünlük Kontrolleri", - "SettingsTabSystemAudioBackend": "Ses Motoru:", + "SettingsTabSystemEnableFsIntegrityChecks": "Dosya Sistemi Bütünlük Kontrolleri", + "SettingsTabSystemAudioBackend": "Ses Arka Ucu:", "SettingsTabSystemAudioBackendDummy": "Yapay", "SettingsTabSystemAudioBackendOpenAL": "OpenAL", "SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hack'ler", "SettingsTabSystemHacksNote": " (dengesizlik oluşturabilir)", - "SettingsTabSystemExpandDramSize": "Alternatif bellek düzeni kullan (Geliştirici)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "Eksik Servisleri Görmezden Gel", "SettingsTabGraphics": "Grafikler", - "SettingsTabGraphicsAPI": "Grafikler API", + "SettingsTabGraphicsAPI": "Grafik API'si", "SettingsTabGraphicsEnableShaderCache": "Shader Önbelleğini Etkinleştir", - "SettingsTabGraphicsAnisotropicFiltering": "Eşyönsüz Doku Süzmesi:", + "SettingsTabGraphicsAnisotropicFiltering": "Anizotropik Filtreleme:", "SettingsTabGraphicsAnisotropicFilteringAuto": "Otomatik", "SettingsTabGraphicsAnisotropicFiltering2x": "2x", "SettingsTabGraphicsAnisotropicFiltering4x": "4x", @@ -179,11 +180,11 @@ "SettingsTabLoggingEnableErrorLogs": "Hata Loglarını Etkinleştir", "SettingsTabLoggingEnableTraceLogs": "Trace Loglarını Etkinleştir", "SettingsTabLoggingEnableGuestLogs": "Guest Loglarını Etkinleştir", - "SettingsTabLoggingEnableFsAccessLogs": "Fs Erişim Loglarını Etkinleştir", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Evrensel Erişim Log Modu:", + "SettingsTabLoggingEnableFsAccessLogs": "Dosya Sistemi Erişim Loglarını Etkinleştir", + "SettingsTabLoggingFsGlobalAccessLogMode": "Dosya Sistemi Evrensel Erişim Log Modu:", "SettingsTabLoggingDeveloperOptions": "Geliştirici Seçenekleri (UYARI: Performansı düşürecektir)", "SettingsTabLoggingDeveloperOptionsNote": "UYARI: Oyun performansı azalacak", - "SettingsTabLoggingGraphicsBackendLogLevel": "Grafik Arka Uç Günlük Düzeyi", + "SettingsTabLoggingGraphicsBackendLogLevel": "Grafik Arka Uç Log Düzeyi", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Hiçbiri", "SettingsTabLoggingGraphicsBackendLogLevelError": "Hata", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Yavaşlamalar", @@ -212,7 +213,7 @@ "ControllerSettingsDeviceDisabled": "Devre Dışı", "ControllerSettingsControllerType": "Kumanda Tipi", "ControllerSettingsControllerTypeHandheld": "Portatif Mod", - "ControllerSettingsControllerTypeProController": "Profesyonel Kumanda", + "ControllerSettingsControllerTypeProController": "Switch Pro Controller ", "ControllerSettingsControllerTypeJoyConPair": "JoyCon Çifti", "ControllerSettingsControllerTypeJoyConLeft": "JoyCon Sol", "ControllerSettingsControllerTypeJoyConRight": "JoyCon Sağ", @@ -271,7 +272,7 @@ "ControllerSettingsMotionGyroDeadzone": "Gyro Ölü Bölgesi:", "ControllerSettingsSave": "Kaydet", "ControllerSettingsClose": "Kapat", - "KeyUnknown": "Unknown", + "KeyUnknown": "Bilinmeyen", "KeyShiftLeft": "Sol Shift", "KeyShiftRight": "Sağ Shift", "KeyControlLeft": "Sol Ctrl", @@ -318,11 +319,11 @@ "KeyKeypad7": "Keypad 7", "KeyKeypad8": "Keypad 8", "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", + "KeyKeypadDivide": "Keypad Bölme İşareti", + "KeyKeypadMultiply": "Keypad Çarpı İşareti", + "KeyKeypadSubtract": "Keypad Eksi İşareti", + "KeyKeypadAdd": "Keypad Artı İşareti", + "KeyKeypadDecimal": "Keypad Nokta ", "KeyKeypadEnter": "Keypad Enter", "KeyNumber0": "0", "KeyNumber1": "1", @@ -346,16 +347,16 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", + "KeyUnbound": "Seçili değil", + "GamepadLeftStick": "Sol Joystick Düğmesi", + "GamepadRightStick": "Sağ Joystick Düğmesi", + "GamepadLeftShoulder": "Sol Shoulder Düğmesi", + "GamepadRightShoulder": "Sağ Shoulder Düğmesi", + "GamepadLeftTrigger": "Sol Trigger Düğmesi", + "GamepadRightTrigger": "Sağ Trigger Düğmesi", + "GamepadDpadUp": "Yukarı", + "GamepadDpadDown": "Aşağı", + "GamepadDpadLeft": "Sol", "GamepadDpadRight": "Sağ", "GamepadMinus": "-", "GamepadPlus": "4", @@ -382,7 +383,7 @@ "ProfileNameSelectionWatermark": "Kullanıcı Adı Seç", "ProfileImageSelectionTitle": "Profil Resmi Seçimi", "ProfileImageSelectionHeader": "Profil Resmi Seç", - "ProfileImageSelectionNote": "Özel bir profil resmi içeri aktarabilir veya sistem avatarlarından birini seçebilirsiniz", + "ProfileImageSelectionNote": "Özel bir profil resmi içeri aktarabilir veya sistem avatarlarından birini seçebilirsiniz.", "ProfileImageSelectionImportImage": "Resim İçeri Aktar", "ProfileImageSelectionSelectAvatar": "Yazılım Avatarı Seç", "InputDialogTitle": "Giriş Yöntemi Diyaloğu", @@ -404,11 +405,12 @@ "GameListContextMenuToggleFavorite": "Favori Ayarla", "GameListContextMenuToggleFavoriteToolTip": "Oyunu Favorilere Ekle/Çıkar", "SettingsTabGeneralTheme": "Tema:", + "SettingsTabGeneralThemeAuto": "Otomatik", "SettingsTabGeneralThemeDark": "Karanlık", "SettingsTabGeneralThemeLight": "Aydınlık", "ControllerSettingsConfigureGeneral": "Ayarla", "ControllerSettingsRumble": "Titreşim", - "ControllerSettingsRumbleStrongMultiplier": "Güçlü Titreşim Çoklayıcı", + "ControllerSettingsRumbleStrongMultiplier": "Güçlü Titreşim Seviyesi", "ControllerSettingsRumbleWeakMultiplier": "Zayıf Titreşim Seviyesi", "DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}] için kayıt verisi bulunamadı", "DialogMessageSaveNotAvailableCreateSaveMessage": "Bu oyun için kayıt verisi oluşturmak ister misiniz?", @@ -446,8 +448,8 @@ "DialogRestartRequiredMessage": "Yeniden Başlatma Gerekli", "DialogThemeRestartMessage": "Tema kaydedildi. Temayı uygulamak için yeniden başlatma gerekiyor.", "DialogThemeRestartSubMessage": "Yeniden başlatmak ister misiniz", - "DialogFirmwareInstallEmbeddedMessage": "Bu oyunun içine gömülü olan yazılımı yüklemek ister misiniz? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", + "DialogFirmwareInstallEmbeddedMessage": "Bu oyunun içine gömülü olan firmware'i yüklemek ister misiniz? (Firmware {0})", + "DialogFirmwareInstallEmbeddedSuccessMessage": "Yüklü firmware bulunamadı ama Ryujinx oyun tarafından sağlanan {0} firmware'ını yükledi.\nEmülatör şimdi başlatılacak.", "DialogFirmwareNoFirmwareInstalledMessage": "Yazılım Yüklü Değil", "DialogFirmwareInstalledMessage": "Yazılım {0} yüklendi", "DialogInstallFileTypesSuccessMessage": "Dosya uzantıları başarıyla yüklendi!", @@ -492,7 +494,7 @@ "DialogControllerSettingsModifiedConfirmSubMessage": "Kaydetmek istiyor musunuz?", "DialogLoadFileErrorMessage": "{0}. Hatalı Dosya: {1}", "DialogModAlreadyExistsMessage": "Mod zaten var", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", + "DialogModInvalidMessage": "Verilen dizinde geçerli bir mod bulunamadı!", "DialogModDeleteNoParentMessage": "Silme Başarısız: \"{0}\" Modu için üst dizin bulunamadı! ", "DialogDlcNoDlcErrorMessage": "Belirtilen dosya seçilen oyun için DLC içermiyor!", "DialogPerformanceCheckLoggingEnabledMessage": "Sadece geliştiriler için dizayn edilen Trace Loglama seçeneği etkin.", @@ -504,8 +506,8 @@ "DialogUpdateAddUpdateErrorMessage": "Belirtilen dosya seçilen oyun için güncelleme içermiyor!", "DialogSettingsBackendThreadingWarningTitle": "Uyarı - Backend Threading", "DialogSettingsBackendThreadingWarningMessage": "Bu seçeneğin tamamen uygulanması için Ryujinx'in kapatıp açılması gerekir. Kullandığınız işletim sistemine bağlı olarak, Ryujinx'in multithreading'ini kullanırken driver'ınızın multithreading seçeneğini kapatmanız gerekebilir.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", + "DialogModManagerDeletionWarningMessage": "{0} modu silinecek.\n\nDevam etmek istediğinizden emin misiniz?", + "DialogModManagerDeletionAllWarningMessage": "Bu oyunun tüm modları silinecek!\n\nDevam etmek istediğinizden emin misiniz?", "SettingsTabGraphicsFeaturesOptions": "Özellikler", "SettingsTabGraphicsBackendMultithreading": "Grafik Backend Multithreading:", "CommonAuto": "Otomatik", @@ -558,13 +560,13 @@ "CustomThemePathTooltip": "Özel arayüz temasının yolu", "CustomThemeBrowseTooltip": "Özel arayüz teması için göz at", "DockModeToggleTooltip": "Docked modu emüle edilen sistemin yerleşik Nintendo Switch gibi davranmasını sağlar. Bu çoğu oyunda grafik kalitesini arttırır. Diğer yandan, bu seçeneği devre dışı bırakmak emüle edilen sistemin portatif Ninendo Switch gibi davranmasını sağlayıp grafik kalitesini düşürür.\n\nDocked modu kullanmayı düşünüyorsanız 1. Oyuncu kontrollerini; Handheld modunu kullanmak istiyorsanız portatif kontrollerini konfigüre edin.\n\nEmin değilseniz aktif halde bırakın.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", + "DirectKeyboardTooltip": "Doğrudan klavye erişimi (HID) desteği. Oyunlara bir yazı giriş cihazı olarak klavyenize erişim sağlar.\n\nSadece Switch donanımı üzerine doğrudan klavye kullanımını destekleyen oyunlarla çalışır.\n\nEğer emin değilseniz, kapalı bırakın.", + "DirectMouseTooltip": "Doğrudan fare erişimi (HID) desteği. Oyunlara bir işaret cihazı olarak farenize erişim sağlar.\n\nSadece Switch donanımı üzerine doğrudan fare kullanımını destekleyen oyunlarla çalışır, bu çok nadirdir.\n\nAçık olduğu zaman, dokunmatik ekran özelliiği çalışmayabilir.\n\nEğer emin değilseniz, kapalı bırakın.", "RegionTooltip": "Sistem Bölgesini Değiştir", "LanguageTooltip": "Sistem Dilini Değiştir", "TimezoneTooltip": "Sistem Saat Dilimini Değiştir", "TimeTooltip": "Sistem Saatini Değiştir", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", + "VSyncToggleTooltip": "Konsolun Dikey Senkronizasyonunu (VSync) emüle et. Çoğu oyunda basitçe bir hız sınırlayıcıdır; bu özelliğin kapatılması bazı oyunların istemeyen derecede hızlı bir şekilde çalışmasını, ya da yükleme ekranlarının daha uzun sürmesini, hatta donmasını tetikleyebilir.\n\nOyun içinde istediğiniz bir kısayol (normalde F1 Tuşu) ile açılıp kapatılabilir. Eğer özelliği kapatmayı planlıyorsanız bunu öneririz.\n\nEğer emin değilseniz özelliği açık bırakın.", "PptcToggleTooltip": "Çevrilen JIT fonksiyonlarını oyun her açıldığında çevrilmek zorunda kalmaması için kaydeder.\n\nTeklemeyi azaltır ve ilk açılıştan sonra oyunların ilk açılış süresini ciddi biçimde hızlandırır.\n\nEmin değilseniz aktif halde bırakın.", "FsIntegrityToggleTooltip": "Oyun açarken hatalı dosyaların olup olmadığını kontrol eder, ve hatalı dosya bulursa log dosyasında hash hatası görüntüler.\n\nPerformansa herhangi bir etkisi yoktur ve sorun gidermeye yardımcı olur.\n\nEmin değilseniz aktif halde bırakın.", "AudioBackendTooltip": "Ses çıkış motorunu değiştirir.\n\nSDL2 tercih edilen seçenektir, OpenAL ve SoundIO ise alternatif olarak kullanılabilir. Dummy seçeneğinde ses çıkışı olmayacaktır.\n\nEmin değilseniz SDL2 seçeneğine ayarlayın.", @@ -573,15 +575,15 @@ "MemoryManagerHostTooltip": "Hafızayı doğrudan host adres aralığında tahsis eder. Çok daha hızlı JIT derleme ve işletimi sunar.", "MemoryManagerUnsafeTooltip": "Hafızayı doğrudan tahsis eder, ancak host aralığına erişimden önce adresi maskelemez. Daha iyi performansa karşılık emniyetten ödün verir. Misafir uygulama Ryujinx içerisinden istediği hafızaya erişebilir, bu sebeple bu seçenek ile sadece güvendiğiniz uygulamaları çalıştırın.", "UseHypervisorTooltip": "JIT yerine Hypervisor kullan. Uygun durumlarda performansı büyük oranda arttırır. Ancak şu anki halinde stabil durumda çalışmayabilir.", - "DRamTooltip": "Emüle edilen sistem hafızasını 4GiB'dan 6GiB'a yükseltir.\n\nBu seçenek yalnızca yüksek çözünürlük doku paketleri veya 4k çözünürlük modları için kullanılır. Performansı artırMAZ!\n\nEmin değilseniz devre dışı bırakın.", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Henüz programlanmamış Horizon işletim sistemi servislerini görmezden gelir. Bu seçenek belirli oyunların açılırken çökmesinin önüne geçmeye yardımcı olabilir.\n\nEmin değilseniz devre dışı bırakın.", "GraphicsBackendThreadingTooltip": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", "GalThreadingTooltip": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", "ShaderCacheToggleTooltip": "Sonraki çalışmalarda takılmaları engelleyen bir gölgelendirici disk önbelleğine kaydeder.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", + "ResolutionScaleTooltip": "Oyunun çözünürlüğünü yükseltir.\n\nBazı oyunlar bu özellikle çalışmayabilir ve çözünürlük artmasına rağmen daha bulanık görülebilir; bu oyunlar için kenar yumuşatmayı kaldıran veya oyunun iç çözünürlüğünü arttıran modlar bulmanız gerekebilir. Eğer iç çözünürlüğü arttıracak modlar kullanacaksanız, bu özelliği \"Yerel (720p/1080p)\"de bırakın.\n\nBu özelliği oyun açıkken \"Uygula\" tuşuna basarak değiştirebilirsiniz. Ayarlar penceresini bir kenara koyun, ve size göre daha iyi görünen seçeneği bulana kadar deneyin.\n\nAklınızda bulunsun ki 4x çoğu bilgisayar için gereğinden yüksek.", "ResolutionScaleEntryTooltip": "Küsüratlı çözünürlük ölçeği, 1.5 gibi. Küsüratlı ölçekler hata oluşturmaya ve çökmeye daha yatkındır.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", + "AnisotropyTooltip": "Anizotropik filtreleme seviyesi. Oyun tarafından istenileni kullanmak için Otomatikte bırakın.", + "AspectRatioTooltip": "Pencereye uygulanan En Boy Oranı.\n\nBu özelliği sadece oyunda En Boy Oranını değiştiren bir mod kullanıyorsanız değiştirin, yoksa grafikler seçtiğiniz En Boy Oranına esnetilecektir.\n\nEğer emin değilseniz 16:9da bırakın.", "ShaderDumpPathTooltip": "Grafik Shader Döküm Yolu", "FileLogTooltip": "Konsol loglarını diskte bir log dosyasına kaydeder. Performansı etkilemez.", "StubLogTooltip": "Stub log mesajlarını konsola yazdırır. Performansı etkilemez.", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "Kurulum Kılavuzunu Aç", "NoUpdate": "Güncelleme Yok", "TitleUpdateVersionLabel": "Sürüm {0} - {1}", + "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", + "TitleBundledDlcLabel": "Bundled:", "RyujinxInfo": "Ryujinx - Bilgi", "RyujinxConfirm": "Ryujinx - Doğrulama", "FileDialogAllTypes": "Tüm türler", @@ -703,7 +707,7 @@ "UserProfileWindowTitle": "Kullanıcı Profillerini Yönet", "CheatWindowTitle": "Oyun Hilelerini Yönet", "DlcWindowTitle": "Oyun DLC'lerini Yönet", - "ModWindowTitle": "Manage Mods for {0} ({1})", + "ModWindowTitle": "{0} için modları yönet ({1})", "UpdateWindowTitle": "Oyun Güncellemelerini Yönet", "CheatWindowHeading": "{0} için Hile mevcut [{1}]", "BuildId": "BuildId:", @@ -723,9 +727,9 @@ "UserProfilesName": "İsim:", "UserProfilesUserId": "Kullanıcı Adı:", "SettingsTabGraphicsBackend": "Grafik Arka Ucu", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", + "SettingsTabGraphicsBackendTooltip": "Emülatörde kullanılacak grafik arka ucunu seçin.\n\nVulkan, sürücüleri güncel olduğu sürece genel olarak tüm modern grafik kartları için daha iyidir. Vulkan ayrıca tüm GPU sağlayıcılarında daha hızlı shader derlemesi (daha az takılma) sunuyor.\n\nOpenGL, eski Nvidia GPU'larda, Linux'taki eski AMD GPU'larda veya daha düşük VRAM'li GPU'larda daha iyi sonuçlar elde edebilir, ancak shader derlemesindeki takılmalar daha fazla olacaktır.\n\nEmin değilseniz Vulkan'ı seçin. GPU'nuz en son grafik sürücülerinde bile Vulkan'ı desteklemiyorsa OpenGL'e ayarlayın.", "SettingsEnableTextureRecompression": "Yeniden Doku Sıkıştırılmasını Aktif Et", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", + "SettingsEnableTextureRecompressionTooltip": "VRAM tüketimini azaltmak için ASTC Dokularını (Texture) sıkıştırır.\n\nBu doku formatını kullanan bazı oyunlar: Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\n4GiB ya da daha az rami olan grafik kartları bu oyunları oynarken çökebilir.\n\nEmin değilseniz kapalı bırakın, sadece belirtilen oyunları oynarken VRAM yetersizliğinden oyun çöküyorsa bu özelliği açın.", "SettingsTabGraphicsPreferredGpu": "Kullanılan GPU", "SettingsTabGraphicsPreferredGpuTooltip": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n", "SettingsAppRequiredRestartMessage": "Ryujinx'i Yeniden Başlatma Gerekli", @@ -751,15 +755,16 @@ "Recover": "Kurtar", "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu", "UserProfilesRecoverEmptyList": "Kurtarılacak profil bulunamadı", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", + "GraphicsAATooltip": "Oyuna Kenar Yumuşatma ekler.\n\nFXAA oyunun neredeyse tamamını daha bulanık hale getirir, SMAA ise keskin kenarları bulup onları yumuşatmaya çalışır.\n\nFSR Çözünürlük Yükseltme Filtresi ile aynı anda kullanılması tavsiye edilmez.\n\nBu özelliği oyun açıkken \"Uygula\" tuşuna basarak değiştirebilirsiniz. Ayarlar penceresini bir kenara koyun, ve size göre daha iyi görünen seçeneği bulana kadar deneyin.\n\nEğer emin değilseniz \"Hiçbiri\"nde bırakın.\n\n", "GraphicsAALabel": "Kenar Yumuşatma:", "GraphicsScalingFilterLabel": "Ölçekleme Filtresi:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", + "GraphicsScalingFilterNearest": "En Yakın", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "Seviye", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", + "GraphicsScalingFilterLevelTooltip": "FSR 1.0 Keskinleştirme seviyesini ayarla. Daha yüksek bir sayı daha keskin demek.", "SmaaLow": "Düşük SMAA", "SmaaMedium": "Orta SMAA", "SmaaHigh": "Yüksek SMAA", @@ -767,14 +772,14 @@ "UserEditorTitle": "Kullanıcıyı Düzenle", "UserEditorTitleCreate": "Kullanıcı Oluştur", "SettingsTabNetworkInterface": "Ağ Bağlantısı:", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", + "NetworkInterfaceTooltip": "LAN/LDN özellikleri için kullanılan ağ arayüzü.\n\nBir VPN ya da XLink Kai ile LAN destekli bir oyunla birlikte, İnternet üzerinden sahte bir aynı ağ bağlantısı kurmak için kullanılabilir.\n\nEmin değilseniz \"Varsayılan\"da bırakın.", "NetworkInterfaceDefault": "Varsayılan", "PackagingShaders": "Gölgeler Paketleniyor", "AboutChangelogButton": "GitHub'da Değişiklikleri Görüntüle", "AboutChangelogButtonTooltipMessage": "Kullandığınız versiyon için olan değişiklikleri varsayılan tarayıcınızda görmek için tıklayın", "SettingsTabNetworkMultiplayer": "Çok Oyunculu", "MultiplayerMode": "Mod:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", + "MultiplayerModeTooltip": "LDN çok oyunculu modunu değiştirin.\n\nLdnMitm, oyunlardaki yerel kablosuz/yerel oyun işlevselliğini LAN gibi çalışacak şekilde değiştirerek diğer Ryujinx uygulamaları ve ldn_mitm modülünün yüklü olduğu hacklenmiş Nintendo Switch konsolları ile yerel, aynı ağ bağlantılarına izin verir.\n\nÇok oyunculu oyunlarda tüm oyuncuların aynı oyun sürümünde olmasını gerektirir (yani mesela Super Smash Bros. Ultimate v13.0.1, v13.0.0'a bağlanamaz).\n\nEmin değilseniz \"devre dışı\" bırakın.", "MultiplayerModeDisabled": "Devre Dışı", "MultiplayerModeLdnMitm": "ldn_mitm" } diff --git a/src/Ryujinx/Assets/Locales/uk_UA.json b/src/Ryujinx/Assets/Locales/uk_UA.json index 976edfb1b..3bbddcfd1 100644 --- a/src/Ryujinx/Assets/Locales/uk_UA.json +++ b/src/Ryujinx/Assets/Locales/uk_UA.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "Використовувати гіпервізор", "MenuBarFile": "_Файл", "MenuBarFileOpenFromFile": "_Завантажити програму з файлу", + "MenuBarFileOpenFromFileError": "У вибраному файлі не знайдено жодного додатку.", "MenuBarFileOpenUnpacked": "Завантажити _розпаковану гру", "MenuBarFileOpenEmuFolder": "Відкрити теку Ryujinx", "MenuBarFileOpenLogsFolder": "Відкрити теку журналів змін", @@ -30,8 +31,8 @@ "MenuBarToolsManageFileTypes": "Керувати типами файлів", "MenuBarToolsInstallFileTypes": "Установити типи файлів", "MenuBarToolsUninstallFileTypes": "Видалити типи файлів", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", + "MenuBarView": "_Вид", + "MenuBarViewWindow": "Розмір Вікна", "MenuBarViewWindow720": "720p", "MenuBarViewWindow1080": "1080p", "MenuBarHelp": "_Допомога", @@ -95,8 +96,8 @@ "SettingsTabGeneralGeneral": "Загальні", "SettingsTabGeneralEnableDiscordRichPresence": "Увімкнути розширену присутність Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Перевіряти наявність оновлень під час запуску", - "SettingsTabGeneralShowConfirmExitDialog": "Показати діалогове вікно «Підтвердити вихід».", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", + "SettingsTabGeneralShowConfirmExitDialog": "Показати діалогове вікно «Підтвердити вихід»", + "SettingsTabGeneralRememberWindowState": "Запам'ятати Розмір Вікна/Положення", "SettingsTabGeneralHideCursor": "Сховати вказівник:", "SettingsTabGeneralHideCursorNever": "Ніколи", "SettingsTabGeneralHideCursorOnIdle": "Сховати у режимі очікування", @@ -143,8 +144,8 @@ "SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Хитрощі", - "SettingsTabSystemHacksNote": " (може викликати нестабільність)", - "SettingsTabSystemExpandDramSize": "Використовувати альтернативне розташування пам'яті (розробники)", + "SettingsTabSystemHacksNote": "(може викликати нестабільність)", + "SettingsTabSystemExpandDramSize": "Розширити DRAM до 8GiB", "SettingsTabSystemIgnoreMissingServices": "Ігнорувати відсутні служби", "SettingsTabGraphics": "Графіка", "SettingsTabGraphicsAPI": "Графічний API", @@ -271,29 +272,29 @@ "ControllerSettingsMotionGyroDeadzone": "Мертва зона гіроскопа:", "ControllerSettingsSave": "Зберегти", "ControllerSettingsClose": "Закрити", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", + "KeyUnknown": "Невідомо", + "KeyShiftLeft": "Лівий Shift", + "KeyShiftRight": "Правий Shift", + "KeyControlLeft": "Лівий Ctrl", + "KeyMacControlLeft": "Лівий ⌃", + "KeyControlRight": "Правий Ctrl", + "KeyMacControlRight": "Правий ⌃", + "KeyAltLeft": "Лівий Alt", + "KeyMacAltLeft": "Лівий ⌥", + "KeyAltRight": "Правий Alt", + "KeyMacAltRight": "Правий ⌥", + "KeyWinLeft": "Лівий ⊞", + "KeyMacWinLeft": "Лівий ⌘", + "KeyWinRight": "Правий ⊞", + "KeyMacWinRight": "Правий ⌘", + "KeyMenu": "Меню", + "KeyUp": "Вгору", + "KeyDown": "Вниз", + "KeyLeft": "Вліво", + "KeyRight": "Вправо", "KeyEnter": "Enter", "KeyEscape": "Escape", - "KeySpace": "Space", + "KeySpace": "Пробіл", "KeyTab": "Tab", "KeyBackSpace": "Backspace", "KeyInsert": "Insert", @@ -308,22 +309,22 @@ "KeyPause": "Pause", "KeyNumLock": "Num Lock", "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", + "KeyKeypad0": "Цифрова клавіша 0", + "KeyKeypad1": "Цифрова клавіша 1", + "KeyKeypad2": "Цифрова клавіша 2", + "KeyKeypad3": "Цифрова клавіша 3", + "KeyKeypad4": "Цифрова клавіша 4", + "KeyKeypad5": "Цифрова клавіша 5", + "KeyKeypad6": "Цифрова клавіша 6", + "KeyKeypad7": "Цифрова клавіша 7", + "KeyKeypad8": "Цифрова клавіша 8", + "KeyKeypad9": "Цифрова клавіша 9", + "KeyKeypadDivide": "Цифрова клавіша /", + "KeyKeypadMultiply": "Цифрова клавіша *", + "KeyKeypadSubtract": "Цифрова клавіша -", + "KeyKeypadAdd": "Цифрова клавіша +", + "KeyKeypadDecimal": "Цифрова клавіша", + "KeyKeypadEnter": "Цифрова клавіша Enter", "KeyNumber0": "0", "KeyNumber1": "1", "KeyNumber2": "2", @@ -346,32 +347,32 @@ "KeyPeriod": ".", "KeySlash": "/", "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", + "KeyUnbound": "Не назначено", + "GamepadLeftStick": "Клавіша лівого стіка", + "GamepadRightStick": "Клавіша правого стіка", + "GamepadLeftShoulder": "Лівий Бампер", + "GamepadRightShoulder": "Правий Бампер", + "GamepadLeftTrigger": "Лівий Тригер", + "GamepadRightTrigger": "Правий Тригер", + "GamepadDpadUp": "Вгору", + "GamepadDpadDown": "Вниз", + "GamepadDpadLeft": "Вліво", + "GamepadDpadRight": "Вправо", "GamepadMinus": "-", "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", + "GamepadGuide": "Посібник", + "GamepadMisc1": "Інше", + "GamepadPaddle1": "Падл 1", + "GamepadPaddle2": "Падл 2", + "GamepadPaddle3": "Падл 3", + "GamepadPaddle4": "Падл 4", + "GamepadTouchpad": "Тачпад", + "GamepadSingleLeftTrigger0": "Лівий Тригер 0", + "GamepadSingleRightTrigger0": "Правий Тригер 0", + "GamepadSingleLeftTrigger1": "Лівий Тригер 1", + "GamepadSingleRightTrigger1": "Правий Тригер 1", + "StickLeft": "Лівий Стік", + "StickRight": "Правий Стік", "UserProfilesSelectedUserProfile": "Вибраний профіль користувача:", "UserProfilesSaveProfileName": "Зберегти ім'я профілю", "UserProfilesChangeProfileImage": "Змінити зображення профілю", @@ -384,7 +385,7 @@ "ProfileImageSelectionHeader": "Виберіть зображення профілю", "ProfileImageSelectionNote": "Ви можете імпортувати власне зображення профілю або вибрати аватар із мікропрограми системи", "ProfileImageSelectionImportImage": "Імпорт файлу зображення", - "ProfileImageSelectionSelectAvatar": "Виберіть аватар прошивки ", + "ProfileImageSelectionSelectAvatar": "Виберіть Аватар Прошивки", "InputDialogTitle": "Діалог введення", "InputDialogOk": "Гаразд", "InputDialogCancel": "Скасувати", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "Перемкнути вибране", "GameListContextMenuToggleFavoriteToolTip": "Перемкнути улюблений статус гри", "SettingsTabGeneralTheme": "Тема:", + "SettingsTabGeneralThemeAuto": "Авто", "SettingsTabGeneralThemeDark": "Темна", "SettingsTabGeneralThemeLight": "Світла", "ControllerSettingsConfigureGeneral": "Налаштування", @@ -573,9 +575,9 @@ "MemoryManagerHostTooltip": "Пряме відображення пам'яті в адресному просторі хосту. Набагато швидша компіляція та виконання JIT.", "MemoryManagerUnsafeTooltip": "Пряме відображення пам’яті, але не маскує адресу в гостьовому адресному просторі перед доступом. Швидше, але ціною безпеки. Гостьова програма може отримати доступ до пам’яті з будь-якого місця в Ryujinx, тому запускайте в цьому режимі лише програми, яким ви довіряєте.", "UseHypervisorTooltip": "Використання гіпервізор замість JIT. Значно покращує продуктивність, коли доступний, але може бути нестабільним у поточному стані.", - "DRamTooltip": "Використовує альтернативний макет MemoryMode для імітації моделі розробки Switch.\n\nЦе корисно лише для пакетів текстур з вищою роздільною здатністю або модифікацій із роздільною здатністю 4K. НЕ покращує продуктивність.\n\nЗалиште вимкненим, якщо не впевнені.", + "DRamTooltip": "Використовує альтернативний режим пам’яті з 8GiB для DRAM щоб імітувати модель Switch для розробника.\n\nЦе корисно лише для пакетів текстур з вищою роздільною здатністю або модифікацій із роздільною здатністю 4k. НЕ покращує продуктивність.\n\nЗалиште Вимкненим, якщо не впевнені.", "IgnoreMissingServicesTooltip": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим, якщо не впевнені.", - "GraphicsBackendThreadingTooltip": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто», якщо не впевнені", + "GraphicsBackendThreadingTooltip": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто», якщо не впевнені.", "GalThreadingTooltip": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто», якщо не впевнені.", "ShaderCacheToggleTooltip": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим, якщо не впевнені.", "ResolutionScaleTooltip": "Множить роздільну здатність гри.\n\nДеякі ігри можуть не працювати з цією функцією, і виглядатимуть піксельними; для цих ігор треба знайти модифікації, що зупиняють згладжування або підвищують роздільну здатність. Для останніх модифікацій, вибирайте \"Native\".\n\nЦей параметр можна міняти коли гра запущена кліком на \"Застосувати\"; ви можете перемістити вікно налаштувань і поекспериментувати з видом гри.\n\nМайте на увазі, що 4x це занадто для будь-якого комп'ютера.", @@ -604,7 +606,7 @@ "OpenProfileManagerTooltip": "Відкриває вікно диспетчера профілів користувачів", "StopEmulationTooltip": "Зупиняє емуляцію поточної гри та повертається до вибору гри", "CheckUpdatesTooltip": "Перевіряє наявність оновлень для Ryujinx", - "OpenAboutTooltip": "Відкриває вікно «Про програму».", + "OpenAboutTooltip": "Відкриває вікно «Про програму»", "GridSize": "Розмір сітки", "GridSizeTooltip": "Змінити розмір елементів сітки", "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Португальська (Бразилія)", @@ -639,15 +641,17 @@ "UserErrorApplicationNotFound": "Додаток не знайдено", "UserErrorUnknown": "Невідома помилка", "UserErrorUndefined": "Невизначена помилка", - "UserErrorNoKeysDescription": "Ryujinx не вдалося знайти ваш файл «prod.keys».", + "UserErrorNoKeysDescription": "Ryujinx не вдалося знайти ваш файл «prod.keys»", "UserErrorNoFirmwareDescription": "Ryujinx не вдалося знайти встановлену прошивку", "UserErrorFirmwareParsingFailedDescription": "Ryujinx не вдалося проаналізувати прошивку. Зазвичай це спричинено застарілими ключами.", - "UserErrorApplicationNotFoundDescription": "Ryujinx не вдалося знайти дійсний додаток за вказаним шляхом", + "UserErrorApplicationNotFoundDescription": "Ryujinx не вдалося знайти дійсний додаток за вказаним шляхом.", "UserErrorUnknownDescription": "Сталася невідома помилка!", "UserErrorUndefinedDescription": "Сталася невизначена помилка! Цього не повинно статися, зверніться до розробника!", "OpenSetupGuideMessage": "Відкрити посібник із налаштування", "NoUpdate": "Немає оновлень", - "TitleUpdateVersionLabel": "Версія {0} - {1}", + "TitleUpdateVersionLabel": "Версія {0}", + "TitleBundledUpdateVersionLabel": "У комплекті: Версія {0}", + "TitleBundledDlcLabel": "У комплекті:", "RyujinxInfo": "Ryujin x - Інформація", "RyujinxConfirm": "Ryujinx - Підтвердження", "FileDialogAllTypes": "Всі типи", @@ -702,12 +706,12 @@ "SelectModDialogTitle": "Виберіть теку з модами", "UserProfileWindowTitle": "Менеджер профілів користувачів", "CheatWindowTitle": "Менеджер читів", - "DlcWindowTitle": "Менеджер вмісту для завантаження", + "DlcWindowTitle": "Менеджер вмісту завантаження для {0} ({1})", "ModWindowTitle": "Керувати модами для {0} ({1})", "UpdateWindowTitle": "Менеджер оновлення назв", "CheatWindowHeading": "Коди доступні для {0} [{1}]", "BuildId": "ID збірки:", - "DlcWindowHeading": "Вміст для завантаження, доступний для {1} ({2}): {0}", + "DlcWindowHeading": "Вміст для завантаження, доступний для: {0}", "ModWindowHeading": "{0} мод(ів)", "UserProfilesEditProfile": "Редагувати вибране", "Cancel": "Скасувати", @@ -717,10 +721,10 @@ "UserProfilesSetProfileImage": "Встановити зображення профілю", "UserProfileEmptyNameError": "Імʼя обовʼязкове", "UserProfileNoImageError": "Зображення профілю обовʼязкове", - "GameUpdateWindowHeading": "{0} Доступні оновлення для {1} ({2})", + "GameUpdateWindowHeading": "Доступні оновлення для {0} ({1})", "SettingsTabHotkeysResScaleUpHotkey": "Збільшити роздільність:", "SettingsTabHotkeysResScaleDownHotkey": "Зменшити роздільність:", - "UserProfilesName": "Імʼя", + "UserProfilesName": "Ім'я:", "UserProfilesUserId": "ID користувача:", "SettingsTabGraphicsBackend": "Графічний сервер", "SettingsTabGraphicsBackendTooltip": "Виберіть backend графіки, що буде використовуватись в емуляторі.\n\n\"Vulkan\" краще для всіх сучасних відеокарт, якщо драйвери вчасно оновлюються. У Vulkan також швидше компілюються шейдери (менше \"заїкання\" зображення) на відеокартах всіх компаній.\n\n\"OpenGL\" може дати кращі результати на старих відеокартах Nvidia, старих відеокартах AMD на Linux, або на відеокартах з маленькою кількістю VRAM, але \"заїкання\" через компіляцію шейдерів будуть частіші.\n\nЯкщо не впевнені, встановіть на \"Vulkan\". Встановіть на \"OpenGL\", якщо Ваша відеокарта не підтримує Vulkan навіть на останніх драйверах.", @@ -742,7 +746,7 @@ "UserProfilesManageSaves": "Керувати збереженнями", "DeleteUserSave": "Ви хочете видалити збереження користувача для цієї гри?", "IrreversibleActionNote": "Цю дію не можна скасувати.", - "SaveManagerHeading": "Керувати збереженнями для {0}", + "SaveManagerHeading": "Керувати збереженнями для {0} ({1})", "SaveManagerTitle": "Менеджер збереження", "Name": "Назва", "Size": "Розмір", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "Застосовує згладження до рендера гри.\n\nFXAA розмиє більшість зображення, а SMAA спробує знайти нерівні краї та згладити їх.\n\nНе рекомендується використовувати разом з фільтром масштабування FSR.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште на \"Немає\", якщо не впевнені.", "GraphicsAALabel": "Згладжування:", "GraphicsScalingFilterLabel": "Фільтр масштабування:", - "GraphicsScalingFilterTooltip": "Виберіть фільтр масштабування, що використається при збільшенні роздільної здатності.\n\n\"Білінійний\" добре виглядає в 3D іграх, і хороше налаштування за умовчуванням.\n\n\"Найближчий\" рекомендується для ігор з піксель-артом.\n\n\"FSR 1.0\" - це просто фільтр різкості, не рекомендується використовувати разом з FXAA або SMAA.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште на \"Білінійний\", якщо не впевнені.", + "GraphicsScalingFilterTooltip": "Виберіть фільтр масштабування, який буде застосовано під час використання масштабу роздільної здатності.\n\nБілінійний добре працює для 3D ігор і є безпечним варіантом за умовчанням.\n\nНайближчий рекомендовано для піксель арт ігор.\n\nFSR 1.0 це лише фільтр різкості, який не рекомендується використовувати з FXAA або SMAA.\n\nМасштабування області рекомендовано при зменшенні роздільності, яка перевищує вікно виведення. Його можна використовувати для досягнення ефекту SSAA при зменшенні більш ніж у 2 рази.\n\nЦей параметр можна змінити коли гра запущена, клацнувши \"Застосувати\" нижче; ви можете просто відсунути вікно налаштувань і експериментувати, поки не знайдете бажаний вигляд гри.\n\nЗалиште на Білінійний, якщо не впевнені.", "GraphicsScalingFilterBilinear": "Білінійний", "GraphicsScalingFilterNearest": "Найближчий", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Область", "GraphicsScalingFilterLevelLabel": "Рівень", "GraphicsScalingFilterLevelTooltip": "Встановити рівень різкості в FSR 1.0. Чим вище - тим різкіше.", "SmaaLow": "SMAA Низький", @@ -774,7 +779,7 @@ "AboutChangelogButtonTooltipMessage": "Клацніть, щоб відкрити журнал змін для цієї версії у стандартному браузері.", "SettingsTabNetworkMultiplayer": "Мережева гра", "MultiplayerMode": "Режим:", - "MultiplayerModeTooltip": "Змінити LDN мультиплеєру.\n\nLdnMitm змінить функціонал бездротової/локальної гри в іграх, щоб вони працювали так, ніби це LAN, що дозволяє локальні підключення в тій самій мережі з іншими екземплярами Ryujinx та хакнутими консолями Nintendo Switch, які мають встановлений модуль ldn_mitm.\n\nМультиплеєр вимагає, щоб усі гравці були на одній і тій же версії гри (наприклад Super Smash Bros. Ultimate v13.0.1 не зможе під'єднатися до v13.0.0).\n\nЗалиште на \"Вимкнено\", якщо не впевнені, ", + "MultiplayerModeTooltip": "Змінити LDN мультиплеєру.\n\nLdnMitm змінить функціонал бездротової/локальної гри в іграх, щоб вони працювали так, ніби це LAN, що дозволяє локальні підключення в тій самій мережі з іншими екземплярами Ryujinx та хакнутими консолями Nintendo Switch, які мають встановлений модуль ldn_mitm.\n\nМультиплеєр вимагає, щоб усі гравці були на одній і тій же версії гри (наприклад Super Smash Bros. Ultimate v13.0.1 не зможе під'єднатися до v13.0.0).\n\nЗалиште на \"Вимкнено\", якщо не впевнені.", "MultiplayerModeDisabled": "Вимкнено", "MultiplayerModeLdnMitm": "ldn_mitm" } diff --git a/src/Ryujinx/Assets/Locales/zh_CN.json b/src/Ryujinx/Assets/Locales/zh_CN.json index 66f59ecd0..85fe463d1 100644 --- a/src/Ryujinx/Assets/Locales/zh_CN.json +++ b/src/Ryujinx/Assets/Locales/zh_CN.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "使用 Hypervisor 虚拟化", "MenuBarFile": "文件(_F)", "MenuBarFileOpenFromFile": "加载游戏文件(_L)", + "MenuBarFileOpenFromFileError": "在所选文件中没有找到应用。", "MenuBarFileOpenUnpacked": "加载解包后的游戏(_U)", "MenuBarFileOpenEmuFolder": "打开 Ryujinx 系统目录", "MenuBarFileOpenLogsFolder": "打开日志目录", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "修改", "SettingsTabSystemHacksNote": "会导致模拟器不稳定", - "SettingsTabSystemExpandDramSize": "使用开发机的内存布局(开发人员使用)", + "SettingsTabSystemExpandDramSize": "扩展DRAM到 8GiB", "SettingsTabSystemIgnoreMissingServices": "忽略缺失的服务", "SettingsTabGraphics": "图形", "SettingsTabGraphicsAPI": "图形 API", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "收藏", "GameListContextMenuToggleFavoriteToolTip": "切换游戏的收藏状态", "SettingsTabGeneralTheme": "主题:", + "SettingsTabGeneralThemeAuto": "跟随系统", "SettingsTabGeneralThemeDark": "深色(暗黑)", "SettingsTabGeneralThemeLight": "浅色(亮色)", "ControllerSettingsConfigureGeneral": "配置", @@ -532,7 +534,7 @@ "AmiiboCharacterLabel": "角色", "AmiiboScanButtonLabel": "扫描", "AmiiboOptionsShowAllLabel": "显示所有 Amiibo", - "AmiiboOptionsUsRandomTagLabel": "修改:使用随机生成的Amiibo ID", + "AmiiboOptionsUsRandomTagLabel": "增强:使用随机生成的Amiibo ID", "DlcManagerTableHeadingEnabledLabel": "已启用", "DlcManagerTableHeadingTitleIdLabel": "游戏 ID", "DlcManagerTableHeadingContainerPathLabel": "容器路径", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "直接映射内存页到电脑内存,使得即时编译和执行的效率更高。", "MemoryManagerUnsafeTooltip": "直接映射内存页到电脑内存,并且不检查内存溢出,使得效率更高,但牺牲了安全。\n游戏程序可以访问模拟器内存的任意地址,所以不安全。\n建议此模式下只运行您信任的游戏程序。", "UseHypervisorTooltip": "使用 Hypervisor 虚拟机代替即时编译,在可用的情况下能大幅提高性能,但目前可能还不稳定。", - "DRamTooltip": "模拟 Switch 开发机的内存布局。\n\n不会提高性能,某些高清纹理包或 4k 分辨率 MOD 可能需要使用此选项。\n\n如果不确定,请保持关闭状态。", + "DRamTooltip": "模拟 Switch 开发机的内存布局(8GiB)。\n\n某些高清纹理包或 4k 分辨率 MOD 可能需要此选项。开启后不会提高性能。\n\n如果不确定,请保持关闭状态。", "IgnoreMissingServicesTooltip": "开启后,游戏会忽略未实现的系统服务,从而继续运行。\n少部分新发布的游戏由于使用了新的未知系统服务,可能需要此选项来避免闪退。\n模拟器更新完善系统服务之后,则无需开启此选项。\n\n如果不确定,请保持关闭状态。", "GraphicsBackendThreadingTooltip": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", "GalThreadingTooltip": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "打开安装指南", "NoUpdate": "无更新(或不加载游戏更新)", "TitleUpdateVersionLabel": "游戏更新的版本 {0}", + "TitleBundledUpdateVersionLabel": "捆绑:版本 {0}", + "TitleBundledDlcLabel": "捆绑:", "RyujinxInfo": "Ryujinx - 信息", "RyujinxConfirm": "Ryujinx - 确认", "FileDialogAllTypes": "全部类型", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "抗锯齿是一种图形处理技术,用于减少图像边缘的锯齿状现象,使图像更加平滑。\n\nFXAA(快速近似抗锯齿)是一种性能开销相对较小的抗锯齿方法,但可能会使得整体图像看起来有些模糊。\n\nSMAA(增强型子像素抗锯齿)则更加精细,它会尝试找到锯齿边缘并平滑它们,相比 FXAA 有更好的图像质量,但性能开销可能会稍大一些。\n\n如果开启了 FSR(FidelityFX Super Resolution,超级分辨率锐画技术)来提高性能或图像质量,不建议再启用抗锯齿,因为它们会产生不必要的图形处理开销,或者相互之间效果不协调。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“无”。", "GraphicsAALabel": "抗锯齿:", "GraphicsScalingFilterLabel": "缩放过滤:", - "GraphicsScalingFilterTooltip": "选择在分辨率缩放时将使用的缩放过滤器。\n\nBilinear(双线性过滤)对于3D游戏效果较好,是一个安全的默认选项。\n\nNearest(最近邻过滤)推荐用于像素艺术游戏。\n\nFSR(超级分辨率锐画)只是一个锐化过滤器,不推荐与 FXAA 或 SMAA 抗锯齿一起使用。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“Bilinear(双线性过滤)”。", + "GraphicsScalingFilterTooltip": "选择在分辨率缩放时将使用的缩放过滤器。\n\nBilinear(双线性过滤)对于3D游戏效果较好,是一个安全的默认选项。\n\nNearest(最近邻过滤)推荐用于像素艺术游戏。\n\nFSR(超级分辨率锐画)只是一个锐化过滤器,不推荐与 FXAA 或 SMAA 抗锯齿一起使用。\n\nArea(局部过滤),当渲染分辨率大于窗口实际分辨率,推荐该选项。该选项在渲染比例大于2.0的情况下,可以实现超采样的效果。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“Bilinear(双线性过滤)”。", "GraphicsScalingFilterBilinear": "Bilinear(双线性过滤)", "GraphicsScalingFilterNearest": "Nearest(最近邻过滤)", "GraphicsScalingFilterFsr": "FSR(超级分辨率锐画技术)", + "GraphicsScalingFilterArea": "Area(局部过滤)", "GraphicsScalingFilterLevelLabel": "等级", "GraphicsScalingFilterLevelTooltip": "设置 FSR 1.0 的锐化等级,数值越高,图像越锐利。", "SmaaLow": "SMAA 低质量", diff --git a/src/Ryujinx/Assets/Locales/zh_TW.json b/src/Ryujinx/Assets/Locales/zh_TW.json index fc838d251..14654c3cb 100644 --- a/src/Ryujinx/Assets/Locales/zh_TW.json +++ b/src/Ryujinx/Assets/Locales/zh_TW.json @@ -10,6 +10,7 @@ "SettingsTabSystemUseHypervisor": "使用 Hypervisor", "MenuBarFile": "檔案(_F)", "MenuBarFileOpenFromFile": "從檔案載入應用程式(_L)", + "MenuBarFileOpenFromFileError": "所選檔案中未找到應用程式。", "MenuBarFileOpenUnpacked": "載入未封裝的遊戲(_U)", "MenuBarFileOpenEmuFolder": "開啟 Ryujinx 資料夾", "MenuBarFileOpenLogsFolder": "開啟日誌資料夾", @@ -144,7 +145,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "補釘修正", "SettingsTabSystemHacksNote": "可能導致模擬器不穩定", - "SettingsTabSystemExpandDramSize": "使用替代的記憶體配置 (開發者專用)", + "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", "SettingsTabSystemIgnoreMissingServices": "忽略缺少的模擬器功能", "SettingsTabGraphics": "圖形", "SettingsTabGraphicsAPI": "圖形 API", @@ -404,6 +405,7 @@ "GameListContextMenuToggleFavorite": "加入/移除為我的最愛", "GameListContextMenuToggleFavoriteToolTip": "切換遊戲的我的最愛狀態", "SettingsTabGeneralTheme": "佈景主題:", + "SettingsTabGeneralThemeAuto": "跟隨系統 (自動)", "SettingsTabGeneralThemeDark": "深色", "SettingsTabGeneralThemeLight": "淺色", "ControllerSettingsConfigureGeneral": "配置", @@ -523,7 +525,7 @@ "AboutGithubUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 GitHub 網頁。", "AboutDiscordUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 Discord 邀請連結。", "AboutTwitterUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 Twitter 網頁。", - "AboutRyujinxAboutTitle": "關於:", + "AboutRyujinxAboutTitle": "關於:", "AboutRyujinxAboutContent": "Ryujinx 是一款 Nintendo Switch™ 模擬器。\n請在 Patreon 上支持我們。\n關注我們的 Twitter 或 Discord 取得所有最新消息。\n對於有興趣貢獻的開發者,可以在我們的 GitHub 或 Discord 上了解更多資訊。", "AboutRyujinxMaintainersTitle": "維護者:", "AboutRyujinxMaintainersContentTooltipMessage": "在預設瀏覽器中開啟貢獻者的網頁", @@ -573,7 +575,7 @@ "MemoryManagerHostTooltip": "直接映射主體位址空間中的記憶體。更快的 JIT 編譯和執行速度。", "MemoryManagerUnsafeTooltip": "直接映射記憶體,但在存取前不封鎖客體位址空間內的位址。速度更快,但相對不安全。訪客應用程式可以從 Ryujinx 中的任何地方存取記憶體,因此只能使用該模式執行您信任的程式。", "UseHypervisorTooltip": "使用 Hypervisor 取代 JIT。使用時可大幅提高效能,但在目前狀態下可能不穩定。", - "DRamTooltip": "利用另一種 MemoryMode 配置來模仿 Switch 開發模式。\n\n這僅對高解析度紋理套件或 4K 解析度模組有用。不會提高效能。\n\n如果不確定,請保持關閉狀態。", + "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "忽略未實現的 Horizon OS 服務。這可能有助於在啟動某些遊戲時避免崩潰。\n\n如果不確定,請保持關閉狀態。", "GraphicsBackendThreadingTooltip": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。", "GalThreadingTooltip": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。", @@ -609,7 +611,7 @@ "GridSizeTooltip": "調整網格的大小", "SettingsTabSystemSystemLanguageBrazilianPortuguese": "巴西葡萄牙文", "AboutRyujinxContributorsButtonHeader": "查看所有貢獻者", - "SettingsTabSystemAudioVolume": "音量:", + "SettingsTabSystemAudioVolume": "音量: ", "AudioVolumeTooltip": "調節音量", "SettingsTabSystemEnableInternetAccess": "訪客網際網路存取/區域網路模式", "EnableInternetAccessTooltip": "允許模擬應用程式連線網際網路。\n\n當啟用此功能且系統連線到同一接入點時,具有區域網路模式的遊戲可相互連線。這也包括真正的遊戲機。\n\n不允許連接 Nintendo 伺服器。可能會導致某些嘗試連線網際網路的遊戲崩潰。\n\n如果不確定,請保持關閉狀態。", @@ -648,6 +650,8 @@ "OpenSetupGuideMessage": "開啟設定指南", "NoUpdate": "沒有更新", "TitleUpdateVersionLabel": "版本 {0}", + "TitleBundledUpdateVersionLabel": "已預裝: 版本 {0}", + "TitleBundledDlcLabel": "已預裝:", "RyujinxInfo": "Ryujinx - 資訊", "RyujinxConfirm": "Ryujinx - 確認", "FileDialogAllTypes": "全部類型", @@ -754,10 +758,11 @@ "GraphicsAATooltip": "對遊戲繪製進行反鋸齒處理。\n\nFXAA 會模糊大部分圖像,而 SMAA 則會嘗試找出鋸齒邊緣並將其平滑化。\n\n不建議與 FSR 縮放濾鏡一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請選擇無狀態。", "GraphicsAALabel": "反鋸齒:", "GraphicsScalingFilterLabel": "縮放過濾器:", - "GraphicsScalingFilterTooltip": "選擇使用解析度縮放時套用的縮放過濾器。\n\n雙線性 (Bilinear) 濾鏡適用於 3D 遊戲,是一個安全的預設選項。\n\n建議像素美術遊戲使用近鄰性 (Nearest) 濾鏡。\n\nFSR 1.0 只是一個銳化濾鏡,不建議與 FXAA 或 SMAA 一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請保持雙線性 (Bilinear) 狀態。", + "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterBilinear": "雙線性 (Bilinear)", "GraphicsScalingFilterNearest": "近鄰性 (Nearest)", "GraphicsScalingFilterFsr": "FSR", + "GraphicsScalingFilterArea": "Area", "GraphicsScalingFilterLevelLabel": "日誌等級", "GraphicsScalingFilterLevelTooltip": "設定 FSR 1.0 銳化等級。越高越清晰。", "SmaaLow": "低階 SMAA", diff --git a/src/Ryujinx/Common/ApplicationHelper.cs b/src/Ryujinx/Common/ApplicationHelper.cs index 14773114c..622a6a024 100644 --- a/src/Ryujinx/Common/ApplicationHelper.cs +++ b/src/Ryujinx/Common/ApplicationHelper.cs @@ -18,8 +18,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Helper; using System; using System.Buffers; @@ -227,11 +226,7 @@ namespace Ryujinx.Ava.Common return; } - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); if (updatePatchNca != null) { patchNca = updatePatchNca; diff --git a/src/Ryujinx/Common/Locale/LocaleExtension.cs b/src/Ryujinx/Common/Locale/LocaleExtension.cs index b5964aa85..40661bf3a 100644 --- a/src/Ryujinx/Common/Locale/LocaleExtension.cs +++ b/src/Ryujinx/Common/Locale/LocaleExtension.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale var builder = new CompiledBindingPathBuilder(); - builder + builder.SetRawSource(LocaleManager.Instance) .Property(new ClrPropertyInfo("Item", obj => (LocaleManager.Instance[keyToUse]), null, @@ -32,10 +32,7 @@ namespace Ryujinx.Ava.Common.Locale var path = builder.Build(); - var binding = new CompiledBindingExtension(path) - { - Source = LocaleManager.Instance - }; + var binding = new CompiledBindingExtension(path); return binding.ProvideValue(serviceProvider); } diff --git a/src/Ryujinx/Common/Locale/LocaleManager.cs b/src/Ryujinx/Common/Locale/LocaleManager.cs index 96f648761..257611e65 100644 --- a/src/Ryujinx/Common/Locale/LocaleManager.cs +++ b/src/Ryujinx/Common/Locale/LocaleManager.cs @@ -139,11 +139,9 @@ namespace Ryujinx.Ava.Common.Locale foreach (var item in locale) { - _localeStrings[item.Key] = item.Value; + this[item.Key] = item.Value; } - OnPropertyChanged("Item"); - LocaleChanged?.Invoke(); } diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 6c83cedcf..f925ce154 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.UI.Common; @@ -81,11 +80,6 @@ namespace Ryujinx.Ava // Parse arguments CommandLineState.ParseArguments(args); - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); @@ -117,8 +111,8 @@ namespace Ryujinx.Ava // Logging system information. PrintSystemInfo(); - // Enable OGL multithreading on the driver, and some other flags. - DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); + // Enable OGL multithreading on the driver, when available. + DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); // Check if keys exists. if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) @@ -131,7 +125,7 @@ namespace Ryujinx.Ava if (CommandLineState.LaunchPathArg != null) { - MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.LaunchApplicationId, CommandLineState.StartFullscreenArg); + MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); } } @@ -154,24 +148,21 @@ namespace Ryujinx.Ava { // No configuration, we load the default values and save it to disk ConfigurationPath = appDataConfigurationPath; - Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}"); ConfigurationState.Instance.LoadDefault(); ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath); } else { - Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}"); - if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat)) { ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath); } else { - Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}"); - ConfigurationState.Instance.LoadDefault(); + + Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}"); } } diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 6718b7fcc..a43f50063 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -54,6 +54,7 @@ + diff --git a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs index 0e7cfb8e6..531d00611 100644 --- a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs @@ -41,12 +41,17 @@ namespace Ryujinx.Ava.UI.Applet private void TextChanged(string text) { - TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false); + TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true); } private void SelectionChanged(int selection) { - TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false); + if (_hiddenTextBox.SelectionEnd < _hiddenTextBox.SelectionStart) + { + _hiddenTextBox.SelectionStart = _hiddenTextBox.SelectionEnd; + } + + TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true); } private void AvaloniaDynamicTextInputHandler_TextInput(object sender, string text) diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs index 5edd02308..894ac6c1a 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs @@ -1,6 +1,7 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; +using Avalonia.Threading; using LibHac.Fs; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common; @@ -14,6 +15,7 @@ using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Helper; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using Path = System.IO.Path; @@ -39,7 +41,7 @@ namespace Ryujinx.Ava.UI.Controls { viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite; - ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.IdString, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata => { appMetadata.Favorite = viewModel.SelectedApplication.Favorite; }); @@ -74,9 +76,19 @@ namespace Ryujinx.Ava.UI.Controls { if (viewModel?.SelectedApplication != null) { - var saveDataFilter = SaveDataFilter.Make(viewModel.SelectedApplication.Id, saveDataType, userId, saveDataId: default, index: default); + if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); + }); - ApplicationHelper.OpenSaveDir(in saveDataFilter, viewModel.SelectedApplication.Id, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.Name); + return; + } + + var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); + + ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName); } } @@ -86,7 +98,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication); + await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } } @@ -96,7 +108,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication); + await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } } @@ -108,8 +120,8 @@ namespace Ryujinx.Ava.UI.Controls { await new CheatWindow( viewModel.VirtualFileSystem, - viewModel.SelectedApplication.IdString, - viewModel.SelectedApplication.Name, + viewModel.SelectedApplication.TitleId, + viewModel.SelectedApplication.TitleName, viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window); } } @@ -121,7 +133,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.IdString); + string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -134,7 +146,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.IdString); + string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -146,7 +158,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await ModManagerWindow.Show(viewModel.SelectedApplication.Id, viewModel.SelectedApplication.Name); + await ModManagerWindow.Show(ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } } @@ -158,15 +170,15 @@ namespace Ryujinx.Ava.UI.Controls { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.Name), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); if (result == UserResult.Yes) { - DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1")); + DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0")); + DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1")); List cacheFiles = new(); @@ -206,14 +218,14 @@ namespace Ryujinx.Ava.UI.Controls { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.Name), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); if (result == UserResult.Yes) { - DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader")); + DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader")); List oldCacheDirectories = new(); List newCacheFiles = new(); @@ -261,7 +273,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu"); + string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu"); string mainDir = Path.Combine(ptcDir, "0"); string backupDir = Path.Combine(ptcDir, "1"); @@ -282,7 +294,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader"); + string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"); if (!Directory.Exists(shaderCacheDir)) { @@ -303,7 +315,7 @@ namespace Ryujinx.Ava.UI.Controls viewModel.StorageProvider, NcaSectionType.Code, viewModel.SelectedApplication.Path, - viewModel.SelectedApplication.Name); + viewModel.SelectedApplication.TitleName); } } @@ -317,7 +329,7 @@ namespace Ryujinx.Ava.UI.Controls viewModel.StorageProvider, NcaSectionType.Data, viewModel.SelectedApplication.Path, - viewModel.SelectedApplication.Name); + viewModel.SelectedApplication.TitleName); } } @@ -331,7 +343,7 @@ namespace Ryujinx.Ava.UI.Controls viewModel.StorageProvider, NcaSectionType.Logo, viewModel.SelectedApplication.Path, - viewModel.SelectedApplication.Name); + viewModel.SelectedApplication.TitleName); } } @@ -342,7 +354,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { ApplicationData selectedApplication = viewModel.SelectedApplication; - ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.Name, selectedApplication.IdString, selectedApplication.Icon); + ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.TitleName, selectedApplication.TitleId, selectedApplication.Icon); } } @@ -352,7 +364,7 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await viewModel.LoadApplication(viewModel.SelectedApplication); + await viewModel.LoadApplication(viewModel.SelectedApplication.Path); } } } diff --git a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml index 98a1c004b..2dc95662a 100644 --- a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml @@ -80,7 +80,7 @@ diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml b/src/Ryujinx/UI/Controls/ApplicationListView.axaml index f99cf316e..fecf08883 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationListView.axaml @@ -85,7 +85,7 @@ typeof(TextBox); - public static RoutedEvent GetKeyDownRoutedEvent() { return KeyDownEvent; diff --git a/src/Ryujinx/UI/Models/DownloadableContentModel.cs b/src/Ryujinx/UI/Models/DownloadableContentModel.cs index 1409d9713..9e400441d 100644 --- a/src/Ryujinx/UI/Models/DownloadableContentModel.cs +++ b/src/Ryujinx/UI/Models/DownloadableContentModel.cs @@ -1,4 +1,3 @@ -using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.ViewModels; using System.IO; @@ -25,9 +24,6 @@ namespace Ryujinx.Ava.UI.Models public string FileName => Path.GetFileName(ContainerPath); - public string Label => - Path.GetExtension(FileName)?.ToLower() == ".xci" ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {FileName}" : FileName; - public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled) { TitleId = titleId; diff --git a/src/Ryujinx/UI/Models/SaveModel.cs b/src/Ryujinx/UI/Models/SaveModel.cs index 181295b06..d6dea2f69 100644 --- a/src/Ryujinx/UI/Models/SaveModel.cs +++ b/src/Ryujinx/UI/Models/SaveModel.cs @@ -46,14 +46,14 @@ namespace Ryujinx.Ava.UI.Models TitleId = info.ProgramId; UserId = info.UserId; - var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.ToUpper() == TitleIdString); + var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.TitleId.ToUpper() == TitleIdString); InGameList = appData != null; if (InGameList) { Icon = appData.Icon; - Title = appData.Name; + Title = appData.TitleName; } else { diff --git a/src/Ryujinx/UI/Models/TitleUpdateModel.cs b/src/Ryujinx/UI/Models/TitleUpdateModel.cs index 46f6f46d8..c270c9ed4 100644 --- a/src/Ryujinx/UI/Models/TitleUpdateModel.cs +++ b/src/Ryujinx/UI/Models/TitleUpdateModel.cs @@ -1,20 +1,18 @@ +using LibHac.Ns; using Ryujinx.Ava.Common.Locale; namespace Ryujinx.Ava.UI.Models { public class TitleUpdateModel { - public uint Version { get; } + public ApplicationControlProperty Control { get; } public string Path { get; } - public string Label { get; } - public TitleUpdateModel(uint version, string displayVersion, string path) + public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleUpdateVersionLabel, Control.DisplayVersionString.ToString()); + + public TitleUpdateModel(ApplicationControlProperty control, string path) { - Version = version; - Label = LocaleManager.Instance.UpdateAndGetDynamicValue( - System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel, - displayVersion - ); + Control = control; Path = path; } } diff --git a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs deleted file mode 100644 index e80984508..000000000 --- a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Ryujinx.UI.App.Common; -using System; - -namespace Ryujinx.Ava.UI.ViewModels -{ - /// - /// Implements a custom comparer which is used for sorting titles by favorite on a UI. - /// Returns a sorted list of favorites in alphabetical order, followed by all non-favorites sorted alphabetical. - /// - public readonly struct AppListFavoriteComparable : IComparable - { - /// - /// The application data being compared. - /// - private readonly ApplicationData app; - - /// - /// Constructs a new with the specified application data. - /// - /// The app data being compared. - public AppListFavoriteComparable(ApplicationData app) - { - ArgumentNullException.ThrowIfNull(app, nameof(app)); - this.app = app; - } - - /// - public readonly int CompareTo(object o) - { - if (o is AppListFavoriteComparable other) - { - if (app.Favorite == other.app.Favorite) - { - return string.Compare(app.Name, other.app.Name, StringComparison.OrdinalIgnoreCase); - } - - return app.Favorite ? -1 : 1; - } - - throw new InvalidCastException($"Cannot cast {o.GetType()} to {nameof(AppListFavoriteComparable)}"); - } - } -} diff --git a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs index c919a7ad1..2cd714f44 100644 --- a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -6,6 +6,7 @@ using DynamicData; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.FsSystem; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; @@ -16,13 +17,11 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using Application = Avalonia.Application; using Path = System.IO.Path; @@ -39,7 +38,7 @@ namespace Ryujinx.Ava.UI.ViewModels private AvaloniaList _selectedDownloadableContents = new(); private string _search; - private readonly ApplicationData _applicationData; + private readonly ulong _titleId; private readonly IStorageProvider _storageProvider; private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); @@ -92,25 +91,18 @@ namespace Ryujinx.Ava.UI.ViewModels get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); } - public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { _virtualFileSystem = virtualFileSystem; - _applicationData = applicationData; + _titleId = titleId; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { _storageProvider = desktop.MainWindow.StorageProvider; } - _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json"); - - if (!File.Exists(_downloadableContentJsonPath)) - { - _downloadableContentContainerList = new List(); - - Save(); - } + _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); try { @@ -131,7 +123,12 @@ namespace Ryujinx.Ava.UI.ViewModels { if (File.Exists(downloadableContentContainer.ContainerPath)) { - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(downloadableContentContainer.ContainerPath, _virtualFileSystem); + using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); + + PartitionFileSystem partitionFileSystem = new(); + partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure(); + + _virtualFileSystem.ImportTickets(partitionFileSystem); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { @@ -160,9 +157,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - // NOTE: Try to load downloadable contents from PFS last to preserve enabled state. - AddDownloadableContent(_applicationData.Path); - // NOTE: Save the list again to remove leftovers. Save(); Sort(); @@ -225,23 +219,25 @@ namespace Ryujinx.Ava.UI.ViewModels foreach (var file in result) { - if (!AddDownloadableContent(file.Path.LocalPath)) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); - } + await AddDownloadableContent(file.Path.LocalPath); } } - private bool AddDownloadableContent(string path) + private async Task AddDownloadableContent(string path) { - if (!File.Exists(path) || _downloadableContentContainerList.Any(x => x.ContainerPath == path)) + if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) { - return true; + return; } - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); + using FileStream containerFile = File.OpenRead(path); + + PartitionFileSystem partitionFileSystem = new(); + partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure(); + bool containsDownloadableContent = false; + + _virtualFileSystem.ImportTickets(partitionFileSystem); - bool success = false; foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) { using var ncaFile = new UniqueRef(); @@ -256,26 +252,26 @@ namespace Ryujinx.Ava.UI.ViewModels if (nca.Header.ContentType == NcaContentType.PublicData) { - if (nca.GetProgramIdBase() != _applicationData.IdBase) + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) { - continue; + break; } var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true); DownloadableContents.Add(content); - Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content)); + SelectedDownloadableContents.Add(content); - success = true; + OnPropertyChanged(nameof(UpdateCount)); + Sort(); + + containsDownloadableContent = true; } } - if (success) + if (!containsDownloadableContent) { - OnPropertyChanged(nameof(UpdateCount)); - Sort(); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); } - - return success; } public void Remove(DownloadableContentModel model) diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index bd9f165b9..549eebf14 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -32,7 +32,7 @@ using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; -using SkiaSharp; +using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -40,6 +40,7 @@ using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; +using Image = SixLabors.ImageSharp.Image; using Key = Ryujinx.Input.Key; using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; @@ -96,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _canUpdate = true; private Cursor _cursor; private string _title; - private ApplicationData _currentApplicationData; + private string _currentEmulatedGamePath; private readonly AutoResetEvent _rendererWaitEvent; private WindowState _windowState; private double _windowWidth; @@ -108,6 +109,7 @@ namespace Ryujinx.Ava.UI.ViewModels public ApplicationData ListSelectedApplication; public ApplicationData GridSelectedApplication; + private string TitleName { get; set; } internal AppHost AppHost { get; set; } public MainWindowViewModel() @@ -953,8 +955,8 @@ namespace Ryujinx.Ava.UI.ViewModels return SortMode switch { #pragma warning disable IDE0055 // Disable formatting - ApplicationSort.Title => IsAscending ? SortExpressionComparer.Ascending(app => app.Name) - : SortExpressionComparer.Descending(app => app.Name), + ApplicationSort.Title => IsAscending ? SortExpressionComparer.Ascending(app => app.TitleName) + : SortExpressionComparer.Descending(app => app.TitleName), ApplicationSort.Developer => IsAscending ? SortExpressionComparer.Ascending(app => app.Developer) : SortExpressionComparer.Descending(app => app.Developer), ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending), @@ -965,8 +967,8 @@ namespace Ryujinx.Ava.UI.ViewModels : SortExpressionComparer.Descending(app => app.FileSize), ApplicationSort.Path => IsAscending ? SortExpressionComparer.Ascending(app => app.Path) : SortExpressionComparer.Descending(app => app.Path), - ApplicationSort.Favorite => IsAscending ? SortExpressionComparer.Ascending(app => new AppListFavoriteComparable(app)) - : SortExpressionComparer.Descending(app => new AppListFavoriteComparable(app)), + ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite) + : SortExpressionComparer.Descending(app => app.Favorite), _ => null, #pragma warning restore IDE0055 }; @@ -998,7 +1000,7 @@ namespace Ryujinx.Ava.UI.ViewModels CompareInfo compareInfo = CultureInfo.CurrentCulture.CompareInfo; - return compareInfo.IndexOf(app.Name, _searchText, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0; + return compareInfo.IndexOf(app.TitleName, _searchText, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0; } return false; @@ -1127,7 +1129,7 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case LoadState.Loaded: - LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; break; @@ -1147,7 +1149,7 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case ShaderCacheLoadingState.Loaded: - LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; break; @@ -1162,17 +1164,17 @@ namespace Ryujinx.Ava.UI.ViewModels private void PrepareLoadScreen() { using MemoryStream stream = new(SelectedIcon); - using var gameIconBmp = SKBitmap.Decode(stream); + using var gameIconBmp = Image.Load(stream); - var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp); + var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel(); const float ColorMultiple = 0.5f; - Color progressFgColor = Color.FromRgb(dominantColor.Red, dominantColor.Green, dominantColor.Blue); + Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B); Color progressBgColor = Color.FromRgb( - (byte)(dominantColor.Red * ColorMultiple), - (byte)(dominantColor.Green * ColorMultiple), - (byte)(dominantColor.Blue * ColorMultiple)); + (byte)(dominantColor.R * ColorMultiple), + (byte)(dominantColor.G * ColorMultiple), + (byte)(dominantColor.B * ColorMultiple)); ProgressBarForegroundColor = new SolidColorBrush(progressFgColor); ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor); @@ -1199,13 +1201,13 @@ namespace Ryujinx.Ava.UI.ViewModels { UserChannelPersistence.ShouldRestart = false; - await LoadApplication(_currentApplicationData); + await LoadApplication(_currentEmulatedGamePath); } else { // Otherwise, clear state. UserChannelPersistence = new UserChannelPersistence(); - _currentApplicationData = null; + _currentEmulatedGamePath = null; } } @@ -1492,15 +1494,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (result.Count > 0) { - if (ApplicationLibrary.TryGetApplicationsFromFile(result[0].Path.LocalPath, - out List applications)) - { - await LoadApplication(applications[0]); - } - else - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]); - } + await LoadApplication(result[0].Path.LocalPath); } } @@ -1514,17 +1508,11 @@ namespace Ryujinx.Ava.UI.ViewModels if (result.Count > 0) { - ApplicationData applicationData = new() - { - Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath), - Path = result[0].Path.LocalPath, - }; - - await LoadApplication(applicationData); + await LoadApplication(result[0].Path.LocalPath); } } - public async Task LoadApplication(ApplicationData application, bool startFullscreen = false) + public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "") { if (AppHost != null) { @@ -1544,7 +1532,7 @@ namespace Ryujinx.Ava.UI.ViewModels Logger.RestartTime(); - SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id); + SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language); PrepareLoadScreen(); @@ -1553,8 +1541,7 @@ namespace Ryujinx.Ava.UI.ViewModels AppHost = new AppHost( RendererHostControl, InputManager, - application.Path, - application.Id, + path, VirtualFileSystem, ContentManager, AccountManager, @@ -1572,17 +1559,17 @@ namespace Ryujinx.Ava.UI.ViewModels CanUpdate = false; - LoadHeading = application.Name; + LoadHeading = TitleName = titleName; - if (string.IsNullOrWhiteSpace(application.Name)) + if (string.IsNullOrWhiteSpace(titleName)) { LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name); - application.Name = AppHost.Device.Processes.ActiveApplication.Name; + TitleName = AppHost.Device.Processes.ActiveApplication.Name; } SwitchToRenderer(startFullscreen); - _currentApplicationData = application; + _currentEmulatedGamePath = path; Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; gameThread.Start(); diff --git a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs index e9b39dfe1..5989ce09a 100644 --- a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Collections; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; @@ -5,7 +6,7 @@ using Avalonia.Threading; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.Ncm; +using LibHac.FsSystem; using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; @@ -16,17 +17,12 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using Application = Avalonia.Application; -using ContentType = LibHac.Ncm.ContentType; using Path = System.IO.Path; using SpanHelpers = LibHac.Common.SpanHelpers; @@ -37,7 +33,7 @@ namespace Ryujinx.Ava.UI.ViewModels public TitleUpdateMetadata TitleUpdateWindowData; public readonly string TitleUpdateJsonPath; private VirtualFileSystem VirtualFileSystem { get; } - private ApplicationData ApplicationData { get; } + private ulong TitleId { get; } private AvaloniaList _titleUpdates = new(); private AvaloniaList _views = new(); @@ -77,18 +73,18 @@ namespace Ryujinx.Ava.UI.ViewModels public IStorageProvider StorageProvider; - public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { VirtualFileSystem = virtualFileSystem; - ApplicationData = applicationData; + TitleId = titleId; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { StorageProvider = desktop.MainWindow.StorageProvider; } - TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdBaseString, "updates.json"); + TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try { @@ -96,7 +92,7 @@ namespace Ryujinx.Ava.UI.ViewModels } catch { - Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdBaseString} at {TitleUpdateJsonPath}"); + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}"); TitleUpdateWindowData = new TitleUpdateMetadata { @@ -112,9 +108,6 @@ namespace Ryujinx.Ava.UI.ViewModels private void LoadUpdates() { - // Try to load updates from PFS first - AddUpdate(ApplicationData.Path, true); - foreach (string path in TitleUpdateWindowData.Paths) { AddUpdate(path); @@ -131,11 +124,26 @@ namespace Ryujinx.Ava.UI.ViewModels public void SortUpdates() { - var sortedUpdates = TitleUpdates.OrderByDescending(update => update.Version); + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) + { + return -1; + } + + if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } + + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); Views.Clear(); Views.Add(new BaseModel()); - Views.AddRange(sortedUpdates); + Views.AddRange(list); if (SelectedUpdate == null) { @@ -154,62 +162,38 @@ namespace Ryujinx.Ava.UI.ViewModels } } - private void AddUpdate(string path, bool ignoreNotFound = false, bool selected = false) + private void AddUpdate(string path) { - if (!File.Exists(path) || TitleUpdates.Any(x => x.Path == path)) + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) { - return; - } + using FileStream file = new(path, FileMode.Open, FileAccess.Read); - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - try - { - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, VirtualFileSystem); - - Dictionary updates = pfs.GetContentData(ContentMetaType.Patch, VirtualFileSystem, checkLevel); - - Nca patchNca = null; - Nca controlNca = null; - - if (updates.TryGetValue(ApplicationData.Id, out ContentMetaData content)) + try { - patchNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Program); - controlNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Control); - } + var pfs = new PartitionFileSystem(); + pfs.Initialize(file.AsStorage()).ThrowIfFailure(); + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0); - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using UniqueRef nacpFile = new(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - var displayVersion = controlData.DisplayVersionString.ToString(); - var update = new TitleUpdateModel(content.Version.Version, displayVersion, path); - - TitleUpdates.Add(update); - - if (selected) + if (controlNca != null && patchNca != null) { - Dispatcher.UIThread.InvokeAsync(() => SelectedUpdate = update); + ApplicationControlProperty controlData = new(); + + using UniqueRef nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); } - } - else - { - if (!ignoreNotFound) + else { Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage])); } } - } - catch (Exception ex) - { - Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, ex.Message, path))); + catch (Exception ex) + { + Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, ex.Message, path))); + } } } @@ -238,7 +222,7 @@ namespace Ryujinx.Ava.UI.ViewModels foreach (var file in result) { - AddUpdate(file.Path.LocalPath, selected: true); + AddUpdate(file.Path.LocalPath); } SortUpdates(); diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index b07bf78b9..89b591229 100644 --- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -9,14 +9,14 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using Color = Avalonia.Media.Color; -using Image = SkiaSharp.SKImage; namespace Ryujinx.Ava.UI.ViewModels { @@ -130,12 +130,9 @@ namespace Ryujinx.Ava.UI.ViewModels stream.Position = 0; - Image avatarImage = Image.FromPixelCopy(new SKImageInfo(256, 256, SKColorType.Rgba8888, SKAlphaType.Premul), DecompressYaz0(stream)); + Image avatarImage = Image.LoadPixelData(DecompressYaz0(stream), 256, 256); - using (SKData data = avatarImage.Encode(SKEncodedImageFormat.Png, 100)) - { - data.SaveTo(streamPng); - } + avatarImage.SaveAsPng(streamPng); _avatarStore.Add(item.FullPath, streamPng.ToArray()); } @@ -154,7 +151,7 @@ namespace Ryujinx.Ava.UI.ViewModels reader.ReadInt64(); // Padding byte[] input = new byte[stream.Length - stream.Position]; - stream.ReadExactly(input, 0, input.Length); + stream.Read(input, 0, input.Length); uint inputOffset = 0; diff --git a/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs index 0b65e6d13..70274847f 100644 --- a/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs @@ -1,7 +1,7 @@ +using Microsoft.IdentityModel.Tokens; using Ryujinx.Ava.UI.Models; using System; using System.Collections.ObjectModel; -using System.Linq; namespace Ryujinx.Ava.UI.ViewModels { @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.ViewModels { Profiles = new ObservableCollection(); LostProfiles = new ObservableCollection(); - IsEmpty = !LostProfiles.Any(); + IsEmpty = LostProfiles.IsNullOrEmpty(); } public ObservableCollection Profiles { get; set; } diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 73ae0df14..522ac19bd 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -11,7 +11,6 @@ using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.Modules; -using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; @@ -135,14 +134,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (!string.IsNullOrEmpty(contentPath)) { - ApplicationData applicationData = new() - { - Name = "miiEdit", - Id = 0x0100000000001009, - Path = contentPath, - }; - - await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen); + await ViewModel.LoadApplication(contentPath, false, "Mii Applet"); } } diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 0a12575ad..5cffc6848 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -1,4 +1,4 @@ - - - - x.BackgroundColor(new Rgba32( + ViewModel.BackgroundColor.R, + ViewModel.BackgroundColor.G, + ViewModel.BackgroundColor.B, + ViewModel.BackgroundColor.A))); + avatarImage.SaveAsJpeg(streamJpg); _profile.Image = streamJpg.ToArray(); diff --git a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs index b4f23b5b8..fabfaa4e8 100644 --- a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -9,9 +9,11 @@ using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; using System.Collections.Generic; using System.IO; +using Image = SixLabors.ImageSharp.Image; namespace Ryujinx.Ava.UI.Views.User { @@ -100,19 +102,13 @@ namespace Ryujinx.Ava.UI.Views.User private static byte[] ProcessProfileImage(byte[] buffer) { - using var bitmap = SKBitmap.Decode(buffer); + using Image image = Image.Load(buffer); - var resizedBitmap = bitmap.Resize(new SKImageInfo(256, 256), SKFilterQuality.High); + image.Mutate(x => x.Resize(256, 256)); - using var streamJpg = new MemoryStream(); + using MemoryStream streamJpg = new(); - if (resizedBitmap != null) - { - using var image = SKImage.FromBitmap(resizedBitmap); - using var dataJpeg = image.Encode(SKEncodedImageFormat.Jpeg, 100); - - dataJpeg.SaveTo(streamJpg); - } + image.SaveAsJpeg(streamJpg); return streamJpg.ToArray(); } diff --git a/src/Ryujinx/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx/UI/Windows/CheatWindow.axaml.cs index 8f4c3cebd..d78e48a4d 100644 --- a/src/Ryujinx/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/CheatWindow.axaml.cs @@ -1,11 +1,9 @@ using Avalonia.Collections; -using LibHac.Tools.FsSystem; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.Globalization; @@ -36,12 +34,9 @@ namespace Ryujinx.Ava.UI.Windows public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath) { LoadedCheats = new AvaloniaList(); - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); - BuildId = ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath); + BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath); InitializeComponent(); diff --git a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml index 98aac09ce..99cf28e77 100644 --- a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml @@ -97,7 +97,7 @@ MaxLines="2" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" - Text="{Binding Label}" /> + Text="{Binding FileName}" /> x.OfType().Name("DialogSpace").Child().OfType()); diff --git a/src/Ryujinx/UI/Windows/IconColorPicker.cs b/src/Ryujinx/UI/Windows/IconColorPicker.cs index dd6a55d4d..72660351a 100644 --- a/src/Ryujinx/UI/Windows/IconColorPicker.cs +++ b/src/Ryujinx/UI/Windows/IconColorPicker.cs @@ -1,4 +1,5 @@ -using SkiaSharp; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; @@ -35,34 +36,35 @@ namespace Ryujinx.Ava.UI.Windows } } - public static SKColor GetFilteredColor(SKBitmap image) + public static Color GetFilteredColor(Image image) { - var color = GetColor(image); - + var color = GetColor(image).ToPixel(); // We don't want colors that are too dark. // If the color is too dark, make it brighter by reducing the range // and adding a constant color. - int luminosity = GetColorApproximateLuminosity(color.Red, color.Green, color.Blue); + int luminosity = GetColorApproximateLuminosity(color.R, color.G, color.B); if (luminosity < CutOffLuminosity) { - color = new SKColor( - (byte)Math.Min(CutOffLuminosity + color.Red, byte.MaxValue), - (byte)Math.Min(CutOffLuminosity + color.Green, byte.MaxValue), - (byte)Math.Min(CutOffLuminosity + color.Blue, byte.MaxValue)); + color = Color.FromRgb( + (byte)Math.Min(CutOffLuminosity + color.R, byte.MaxValue), + (byte)Math.Min(CutOffLuminosity + color.G, byte.MaxValue), + (byte)Math.Min(CutOffLuminosity + color.B, byte.MaxValue)); } return color; } - public static SKColor GetColor(SKBitmap image) + public static Color GetColor(Image image) { var colors = new PaletteColor[TotalColors]; + var dominantColorBin = new Dictionary(); var buffer = GetBuffer(image); int w = image.Width; + int w8 = w << 8; int h8 = image.Height << 8; @@ -82,10 +84,9 @@ namespace Ryujinx.Ava.UI.Windows { int offset = x + yOffset; - SKColor pixel = buffer[offset]; - byte cr = pixel.Red; - byte cg = pixel.Green; - byte cb = pixel.Blue; + byte cb = buffer[offset].B; + byte cg = buffer[offset].G; + byte cr = buffer[offset].R; var qck = GetQuantizedColorKey(cr, cg, cb); @@ -121,22 +122,12 @@ namespace Ryujinx.Ava.UI.Windows } } - return new SKColor(bestCandidate.R, bestCandidate.G, bestCandidate.B); + return Color.FromRgb(bestCandidate.R, bestCandidate.G, bestCandidate.B); } - public static SKColor[] GetBuffer(SKBitmap image) + public static Bgra32[] GetBuffer(Image image) { - var pixels = new SKColor[image.Width * image.Height]; - - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - pixels[x + y * image.Width] = image.GetPixel(x, y); - } - } - - return pixels; + return image.DangerousTryGetSinglePixelMemory(out var data) ? data.ToArray() : Array.Empty(); } private static int GetColorScore(Dictionary dominantColorBin, int maxHitCount, PaletteColor color) diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml b/src/Ryujinx/UI/Windows/MainWindow.axaml index 3a2e02c26..6c2042f93 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml @@ -42,10 +42,12 @@ + - + diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 348412e78..7de8a49a0 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -5,7 +5,6 @@ using Avalonia.Interactivity; using Avalonia.Platform; using Avalonia.Threading; using FluentAvalonia.UI.Controls; -using LibHac.Tools.FsSystem; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; @@ -25,7 +24,7 @@ using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; using System; -using System.Collections.Generic; +using System.IO; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; @@ -37,12 +36,10 @@ namespace Ryujinx.Ava.UI.Windows internal static MainWindowViewModel MainWindowViewModel { get; private set; } private bool _isLoading; - private bool _applicationsLoadedOnce; private UserChannelPersistence _userChannelPersistence; private static bool _deferLoad; private static string _launchPath; - private static string _launchApplicationId; private static bool _startFullscreen; internal readonly AvaHostUIHandler UiHandler; @@ -171,17 +168,18 @@ namespace Ryujinx.Ava.UI.Windows { ViewModel.SelectedIcon = args.Application.Icon; - ViewModel.LoadApplication(args.Application).Wait(); + string path = new FileInfo(args.Application.Path).FullName; + + ViewModel.LoadApplication(path).Wait(); } args.Handled = true; } - internal static void DeferLoadApplication(string launchPathArg, string launchApplicationId, bool startFullscreenArg) + internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg) { _deferLoad = true; _launchPath = launchPathArg; - _launchApplicationId = launchApplicationId; _startFullscreen = startFullscreenArg; } @@ -221,14 +219,7 @@ namespace Ryujinx.Ava.UI.Windows LibHacHorizonManager.InitializeBcatServer(); LibHacHorizonManager.InitializeSystemClients(); - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem, checkLevel) - { - DesiredLanguage = ConfigurationState.Instance.System.Language, - }; + ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem); // Save data created before we supported extra data in directory save data will not work properly if // given empty extra data. Luckily some of that extra data can be created using the data from the @@ -323,35 +314,7 @@ namespace Ryujinx.Ava.UI.Windows { _deferLoad = false; - if (ApplicationLibrary.TryGetApplicationsFromFile(_launchPath, out List applications)) - { - ApplicationData applicationData; - - if (_launchApplicationId != null) - { - applicationData = applications.Find(application => application.IdString == _launchApplicationId); - - if (applicationData != null) - { - await ViewModel.LoadApplication(applicationData, _startFullscreen); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find requested application id '{_launchApplicationId}' in '{_launchPath}'."); - await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.ApplicationNotFound)); - } - } - else - { - applicationData = applications[0]; - await ViewModel.LoadApplication(applicationData, _startFullscreen); - } - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find any application in '{_launchPath}'."); - await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.ApplicationNotFound)); - } + await ViewModel.LoadApplication(_launchPath, _startFullscreen); } } else @@ -476,11 +439,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.RefreshFirmwareStatus(); - // Load applications if no application was requested by the command line - if (!_deferLoad) - { - LoadApplications(); - } + LoadApplications(); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed CheckLaunchState(); @@ -493,12 +452,6 @@ namespace Ryujinx.Ava.UI.Windows if (MainContent.Content != content) { - // Load applications while switching to the GameLibrary if we haven't done that yet - if (!_applicationsLoadedOnce && content == GameLibrary) - { - LoadApplications(); - } - MainContent.Content = content; } } @@ -595,7 +548,6 @@ namespace Ryujinx.Ava.UI.Windows public void LoadApplications() { - _applicationsLoadedOnce = true; ViewModel.Applications.Clear(); StatusBarView.LoadProgressBar.IsVisible = true; @@ -637,8 +589,7 @@ namespace Ryujinx.Ava.UI.Windows Thread applicationLibraryThread = new(() => { - ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs); + ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); _isLoading = false; }) diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs index af917e7f3..f5e250323 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs @@ -5,18 +5,19 @@ using Avalonia.Interactivity; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; -using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Helper; using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Windows { public partial class TitleUpdateWindow : UserControl { - public readonly TitleUpdateViewModel ViewModel; + public TitleUpdateViewModel ViewModel; public TitleUpdateWindow() { @@ -25,22 +26,22 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); } - public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId) { - DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationData); + DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId); InitializeComponent(); } - public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { ContentDialog contentDialog = new() { PrimaryButtonText = "", SecondaryButtonText = "", CloseButtonText = "", - Content = new TitleUpdateWindow(virtualFileSystem, applicationData), - Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString), + Content = new TitleUpdateWindow(virtualFileSystem, titleId), + Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")), }; Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType());