From a77af4c5e9e74ada62bfdb7ef5672eeebb00c3ab Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 19 Sep 2022 14:06:00 +0200 Subject: [PATCH 1/8] Readme: Fix broken shell image (#3708) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83f56f66a..9a8581e06 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@

- +

From b9f1ff3c7748c6a2665e76d17e86c3b7228f44fe Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:49:10 +0200 Subject: [PATCH 2/8] Implemented in IR the managed methods of the ShlReg region of the SoftFallback class. (#3700) * Implemented in IR the managed methods of the Saturating region ... ... of the SoftFallback class (the SatQ ones). The need to natively manage the Fpcr and Fpsr system registers is still a fact. Contributes to https://github.com/Ryujinx/Ryujinx/issues/2917 ; I will open another PR to implement in Intrinsics-branchless the methods of the Saturation region as well (the SatXXXToXXX ones). All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq. * Ptc.InternalVersion = 3665 * Addressed PR feedback. * Implemented in IR the managed methods of the ShlReg region of the SoftFallback class. It also includes the last two SatQ ones (following up on https://github.com/Ryujinx/Ryujinx/pull/3665). All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq. * Update InstEmitSimdHelper.cs --- .../Instructions/InstEmitSimdHelper.cs | 156 +++++-- ARMeilleure/Instructions/InstEmitSimdShift.cs | 386 +++++++++++------- ARMeilleure/Instructions/SoftFallback.cs | 330 --------------- ARMeilleure/Translation/Delegates.cs | 4 - ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 5 files changed, 351 insertions(+), 527 deletions(-) diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 27b5c1302..80dfc6889 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -1533,29 +1533,88 @@ namespace ARMeilleure.Instructions context.Copy(d, res); } - // TSrc (16bit, 32bit, 64bit; signed) > TDst (8bit, 16bit, 32bit; signed, unsigned). - // long SignedSrcSignedDstSatQ(long op, int size); ulong SignedSrcUnsignedDstSatQ(long op, int size); - public static Operand EmitSignedSrcSatQ(ArmEmitterContext context, Operand op, int sizeDst, bool signedDst) + // long SignedSignSatQ(long op, int size); + public static Operand EmitSignedSignSatQ(ArmEmitterContext context, Operand op, int size) { - Debug.Assert(op.Type == OperandType.I64 && (uint)sizeDst <= 2u); + int eSize = 8 << size; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64); Operand lbl1 = Label(); Operand lblEnd = Label(); - int eSize = 8 << sizeDst; + Operand zeroL = Const(0L); + Operand maxT = Const((1L << (eSize - 1)) - 1L); + Operand minT = Const(-(1L << (eSize - 1))); - Operand maxT = signedDst ? Const((1L << (eSize - 1)) - 1L) : Const((1UL << eSize) - 1UL); - Operand minT = signedDst ? Const(-(1L << (eSize - 1))) : Const(0UL); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), zeroL); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); - - context.BranchIf(lbl1, res, maxT, Comparison.LessOrEqual); + context.BranchIf(lbl1, op, zeroL, Comparison.LessOrEqual); context.Copy(res, maxT); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); context.MarkLabel(lbl1); - context.BranchIf(lblEnd, res, minT, Comparison.GreaterOrEqual); + context.BranchIf(lblEnd, op, zeroL, Comparison.GreaterOrEqual); + context.Copy(res, minT); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + + // private static ulong UnsignedSignSatQ(ulong op, int size); + public static Operand EmitUnsignedSignSatQ(ArmEmitterContext context, Operand op, int size) + { + int eSize = 8 << size; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64); + + Operand lblEnd = Label(); + + Operand zeroUL = Const(0UL); + Operand maxT = Const(ulong.MaxValue >> (64 - eSize)); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), zeroUL); + + context.BranchIf(lblEnd, op, zeroUL, Comparison.LessOrEqualUI); + context.Copy(res, maxT); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + + // TSrc (16bit, 32bit, 64bit; signed) > TDst (8bit, 16bit, 32bit; signed, unsigned). + // long SignedSrcSignedDstSatQ(long op, int size); ulong SignedSrcUnsignedDstSatQ(long op, int size); + public static Operand EmitSignedSrcSatQ(ArmEmitterContext context, Operand op, int sizeDst, bool signedDst) + { + int eSizeDst = 8 << sizeDst; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32); + + Operand lbl1 = Label(); + Operand lblEnd = Label(); + + Operand maxT = signedDst ? Const((1L << (eSizeDst - 1)) - 1L) : Const((1UL << eSizeDst) - 1UL); + Operand minT = signedDst ? Const(-(1L << (eSizeDst - 1))) : Const(0UL); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); + + context.BranchIf(lbl1, op, maxT, Comparison.LessOrEqual); + context.Copy(res, maxT); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); + context.Branch(lblEnd); + + context.MarkLabel(lbl1); + context.BranchIf(lblEnd, op, minT, Comparison.GreaterOrEqual); context.Copy(res, minT); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1569,18 +1628,19 @@ namespace ARMeilleure.Instructions // long UnsignedSrcSignedDstSatQ(ulong op, int size); ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size); public static Operand EmitUnsignedSrcSatQ(ArmEmitterContext context, Operand op, int sizeDst, bool signedDst) { - Debug.Assert(op.Type == OperandType.I64 && (uint)sizeDst <= 2u); + int eSizeDst = 8 << sizeDst; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32); Operand lblEnd = Label(); - int eSize = 8 << sizeDst; - - Operand maxL = signedDst ? Const((1L << (eSize - 1)) - 1L) : Const((1UL << eSize) - 1UL); + Operand maxT = signedDst ? Const((1L << (eSizeDst - 1)) - 1L) : Const((1UL << eSizeDst) - 1UL); Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); - context.BranchIf(lblEnd, res, maxL, Comparison.LessOrEqualUI); - context.Copy(res, maxL); + context.BranchIf(lblEnd, op, maxT, Comparison.LessOrEqualUI); + context.Copy(res, maxT); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1601,7 +1661,7 @@ namespace ARMeilleure.Instructions Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); - context.BranchIf(lblEnd, res, minL, Comparison.NotEqual); + context.BranchIf(lblEnd, op, minL, Comparison.NotEqual); context.Copy(res, maxL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1620,15 +1680,16 @@ namespace ARMeilleure.Instructions Operand minL = Const(long.MinValue); Operand maxL = Const(long.MaxValue); - Operand zero = Const(0L); + Operand zeroL = Const(0L); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2)); + Operand add = context.Add(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), add); Operand left = context.BitwiseNot(context.BitwiseExclusiveOr(op1, op2)); - Operand right = context.BitwiseExclusiveOr(op1, res); - context.BranchIf(lblEnd, context.BitwiseAnd(left, right), zero, Comparison.GreaterOrEqual); + Operand right = context.BitwiseExclusiveOr(op1, add); + context.BranchIf(lblEnd, context.BitwiseAnd(left, right), zeroL, Comparison.GreaterOrEqual); - Operand isPositive = context.ICompareGreaterOrEqual(op1, zero); + Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL); context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL)); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1647,9 +1708,10 @@ namespace ARMeilleure.Instructions Operand maxUL = Const(ulong.MaxValue); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2)); + Operand add = context.Add(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), add); - context.BranchIf(lblEnd, res, op1, Comparison.GreaterOrEqualUI); + context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI); context.Copy(res, maxUL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1668,15 +1730,16 @@ namespace ARMeilleure.Instructions Operand minL = Const(long.MinValue); Operand maxL = Const(long.MaxValue); - Operand zero = Const(0L); + Operand zeroL = Const(0L); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Subtract(op1, op2)); + Operand sub = context.Subtract(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), sub); Operand left = context.BitwiseExclusiveOr(op1, op2); - Operand right = context.BitwiseExclusiveOr(op1, res); - context.BranchIf(lblEnd, context.BitwiseAnd(left, right), zero, Comparison.GreaterOrEqual); + Operand right = context.BitwiseExclusiveOr(op1, sub); + context.BranchIf(lblEnd, context.BitwiseAnd(left, right), zeroL, Comparison.GreaterOrEqual); - Operand isPositive = context.ICompareGreaterOrEqual(op1, zero); + Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL); context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL)); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1693,12 +1756,13 @@ namespace ARMeilleure.Instructions Operand lblEnd = Label(); - Operand zero = Const(0L); + Operand zeroL = Const(0L); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Subtract(op1, op2)); + Operand sub = context.Subtract(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), sub); context.BranchIf(lblEnd, op1, op2, Comparison.GreaterOrEqualUI); - context.Copy(res, zero); + context.Copy(res, zeroL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1717,25 +1781,26 @@ namespace ARMeilleure.Instructions Operand lblEnd = Label(); Operand maxL = Const(long.MaxValue); - Operand zero = Const(0L); + Operand zeroL = Const(0L); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2)); + Operand add = context.Add(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), add); context.BranchIf(lbl1, op1, maxL, Comparison.GreaterUI); - Operand notOp2AndRes = context.BitwiseAnd(context.BitwiseNot(op2), res); - context.BranchIf(lblEnd, notOp2AndRes, zero, Comparison.GreaterOrEqual); + Operand notOp2AndRes = context.BitwiseAnd(context.BitwiseNot(op2), add); + context.BranchIf(lblEnd, notOp2AndRes, zeroL, Comparison.GreaterOrEqual); context.Copy(res, maxL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); context.MarkLabel(lbl1); - context.BranchIf(lbl2, op2, zero, Comparison.Less); + context.BranchIf(lbl2, op2, zeroL, Comparison.Less); context.Copy(res, maxL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); context.MarkLabel(lbl2); - context.BranchIf(lblEnd, res, maxL, Comparison.LessOrEqualUI); + context.BranchIf(lblEnd, add, maxL, Comparison.LessOrEqualUI); context.Copy(res, maxL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); @@ -1755,20 +1820,21 @@ namespace ARMeilleure.Instructions Operand maxUL = Const(ulong.MaxValue); Operand maxL = Const(long.MaxValue); - Operand zero = Const(0L); + Operand zeroL = Const(0L); - Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2)); + Operand add = context.Add(op1, op2); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), add); - context.BranchIf(lbl1, op1, zero, Comparison.Less); - context.BranchIf(lblEnd, res, op1, Comparison.GreaterOrEqualUI); + context.BranchIf(lbl1, op1, zeroL, Comparison.Less); + context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI); context.Copy(res, maxUL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); context.MarkLabel(lbl1); context.BranchIf(lblEnd, op2, maxL, Comparison.GreaterUI); - context.BranchIf(lblEnd, res, zero, Comparison.GreaterOrEqual); - context.Copy(res, zero); + context.BranchIf(lblEnd, add, zeroL, Comparison.GreaterOrEqual); + context.Copy(res, zeroL); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.Branch(lblEnd); diff --git a/ARMeilleure/Instructions/InstEmitSimdShift.cs b/ARMeilleure/Instructions/InstEmitSimdShift.cs index 1a95200db..146aeafa7 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift.cs @@ -188,23 +188,7 @@ namespace ARMeilleure.Instructions public static void Sqrshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ)), ne, me, Const(1), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round | ShlRegFlags.Saturating); } public static void Sqrshrn_S(ArmEmitterContext context) @@ -229,23 +213,7 @@ namespace ARMeilleure.Instructions public static void Sqshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ)), ne, me, Const(0), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Saturating); } public static void Sqshrn_S(ArmEmitterContext context) @@ -280,23 +248,7 @@ namespace ARMeilleure.Instructions public static void Srshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg)), ne, me, Const(1), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round); } public static void Srshr_S(ArmEmitterContext context) @@ -393,12 +345,12 @@ namespace ARMeilleure.Instructions public static void Sshl_S(ArmEmitterContext context) { - EmitSshlOrUshl(context, signed: true, scalar: true); + EmitShlRegOp(context, ShlRegFlags.Scalar | ShlRegFlags.Signed); } public static void Sshl_V(ArmEmitterContext context) { - EmitSshlOrUshl(context, signed: true, scalar: false); + EmitShlRegOp(context, ShlRegFlags.Signed); } public static void Sshll_V(ArmEmitterContext context) @@ -506,23 +458,7 @@ namespace ARMeilleure.Instructions public static void Uqrshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)), ne, me, Const(1), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Round | ShlRegFlags.Saturating); } public static void Uqrshrn_S(ArmEmitterContext context) @@ -537,23 +473,7 @@ namespace ARMeilleure.Instructions public static void Uqshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)), ne, me, Const(0), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Saturating); } public static void Uqshrn_S(ArmEmitterContext context) @@ -568,23 +488,7 @@ namespace ARMeilleure.Instructions public static void Urshl_V(ArmEmitterContext context) { - OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - for (int index = 0; index < elems; index++) - { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - - Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg)), ne, me, Const(1), Const(op.Size)); - - res = EmitVectorInsert(context, res, e, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); + EmitShlRegOp(context, ShlRegFlags.Round); } public static void Urshr_S(ArmEmitterContext context) @@ -677,12 +581,12 @@ namespace ARMeilleure.Instructions public static void Ushl_S(ArmEmitterContext context) { - EmitSshlOrUshl(context, signed: false, scalar: true); + EmitShlRegOp(context, ShlRegFlags.Scalar); } public static void Ushl_V(ArmEmitterContext context) { - EmitSshlOrUshl(context, signed: false, scalar: false); + EmitShlRegOp(context, ShlRegFlags.None); } public static void Ushll_V(ArmEmitterContext context) @@ -872,43 +776,6 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool signed) - { - Debug.Assert(op.Type == OperandType.I64); - Debug.Assert(shiftLsB.Type == OperandType.I32); - Debug.Assert((uint)size < 4u); - - Operand negShiftLsB = context.Negate(shiftLsB); - - Operand isInRange = context.BitwiseAnd( - context.ICompareLess(shiftLsB, Const(8 << size)), - context.ICompareLess(negShiftLsB, Const(8 << size))); - - Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0)); - - Operand shl = context.ShiftLeft(op, shiftLsB); - - Operand sarOrShr = signed - ? context.ShiftRightSI(op, negShiftLsB) - : context.ShiftRightUI(op, negShiftLsB); - - Operand res = context.ConditionalSelect(isPositive, shl, sarOrShr); - - if (signed) - { - Operand isPositive2 = context.ICompareGreaterOrEqual(op, Const(0L)); - - Operand res2 = context.ConditionalSelect(isPositive2, Const(0L), Const(-1L)); - res2 = context.ConditionalSelect(isPositive, Const(0L), res2); - - return context.ConditionalSelect(isInRange, res, res2); - } - else - { - return context.ConditionalSelect(isInRange, res, Const(0UL)); - } - } - private static void EmitVectorShrImmNarrowOpZx(ArmEmitterContext context, bool round) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; @@ -1168,8 +1035,23 @@ namespace ARMeilleure.Instructions } } - private static void EmitSshlOrUshl(ArmEmitterContext context, bool signed, bool scalar) + [Flags] + private enum ShlRegFlags { + None = 0, + Scalar = 1 << 0, + Signed = 1 << 1, + Round = 1 << 2, + Saturating = 1 << 3 + } + + private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None) + { + bool scalar = flags.HasFlag(ShlRegFlags.Scalar); + bool signed = flags.HasFlag(ShlRegFlags.Signed); + bool round = flags.HasFlag(ShlRegFlags.Round); + bool saturating = flags.HasFlag(ShlRegFlags.Saturating); + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; Operand res = context.VectorZero(); @@ -1178,15 +1060,225 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { - Operand ne = EmitVectorExtract (context, op.Rn, index, op.Size, signed); - Operand me = EmitVectorExtractSx(context, op.Rm, index << op.Size, 0); + Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size, signed); + Operand me = EmitVectorExtractSx(context, op.Rm, index << op.Size, size: 0); - Operand e = EmitShlRegOp(context, ne, context.ConvertI64ToI32(me), op.Size, signed); + Operand e = !saturating + ? EmitShlReg(context, ne, context.ConvertI64ToI32(me), round, op.Size, signed) + : EmitShlRegSatQ(context, ne, context.ConvertI64ToI32(me), round, op.Size, signed); res = EmitVectorInsert(context, res, e, index, op.Size); } context.Copy(GetVec(op.Rd), res); } + + // long SignedShlReg(long op, int shiftLsB, bool round, int size); + // ulong UnsignedShlReg(ulong op, int shiftLsB, bool round, int size); + private static Operand EmitShlReg(ArmEmitterContext context, Operand op, Operand shiftLsB, bool round, int size, bool signed) + { + int eSize = 8 << size; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(shiftLsB.Type == OperandType.I32); + Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64); + + Operand lbl1 = Label(); + Operand lblEnd = Label(); + + Operand eSizeOp = Const(eSize); + Operand zero = Const(0); + Operand zeroL = Const(0L); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); + + context.BranchIf(lbl1, shiftLsB, zero, Comparison.GreaterOrEqual); + context.Copy(res, signed + ? EmitSignedShrReg(context, op, context.Negate(shiftLsB), round, eSize) + : EmitUnsignedShrReg(context, op, context.Negate(shiftLsB), round, eSize)); + context.Branch(lblEnd); + + context.MarkLabel(lbl1); + context.BranchIf(lblEnd, shiftLsB, zero, Comparison.LessOrEqual); + Operand shl = context.ShiftLeft(op, shiftLsB); + Operand isGreaterOrEqual = context.ICompareGreaterOrEqual(shiftLsB, eSizeOp); + context.Copy(res, context.ConditionalSelect(isGreaterOrEqual, zeroL, shl)); + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + + // long SignedShlRegSatQ(long op, int shiftLsB, bool round, int size); + // ulong UnsignedShlRegSatQ(ulong op, int shiftLsB, bool round, int size); + private static Operand EmitShlRegSatQ(ArmEmitterContext context, Operand op, Operand shiftLsB, bool round, int size, bool signed) + { + int eSize = 8 << size; + + Debug.Assert(op.Type == OperandType.I64); + Debug.Assert(shiftLsB.Type == OperandType.I32); + Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64); + + Operand lbl1 = Label(); + Operand lbl2 = Label(); + Operand lblEnd = Label(); + + Operand eSizeOp = Const(eSize); + Operand zero = Const(0); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op); + + context.BranchIf(lbl1, shiftLsB, zero, Comparison.GreaterOrEqual); + context.Copy(res, signed + ? EmitSignedShrReg(context, op, context.Negate(shiftLsB), round, eSize) + : EmitUnsignedShrReg(context, op, context.Negate(shiftLsB), round, eSize)); + context.Branch(lblEnd); + + context.MarkLabel(lbl1); + context.BranchIf(lblEnd, shiftLsB, zero, Comparison.LessOrEqual); + context.BranchIf(lbl2, shiftLsB, eSizeOp, Comparison.Less); + context.Copy(res, signed + ? EmitSignedSignSatQ(context, op, size) + : EmitUnsignedSignSatQ(context, op, size)); + context.Branch(lblEnd); + + context.MarkLabel(lbl2); + Operand shl = context.ShiftLeft(op, shiftLsB); + if (eSize == 64) + { + Operand sarOrShr = signed + ? context.ShiftRightSI(shl, shiftLsB) + : context.ShiftRightUI(shl, shiftLsB); + context.Copy(res, shl); + context.BranchIf(lblEnd, sarOrShr, op, Comparison.Equal); + context.Copy(res, signed + ? EmitSignedSignSatQ(context, op, size) + : EmitUnsignedSignSatQ(context, op, size)); + } + else + { + context.Copy(res, signed + ? EmitSignedSrcSatQ(context, shl, size, signedDst: true) + : EmitUnsignedSrcSatQ(context, shl, size, signedDst: false)); + } + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + + // shift := [1, 128]; eSize := {8, 16, 32, 64}. + // long SignedShrReg(long op, int shift, bool round, int eSize); + private static Operand EmitSignedShrReg(ArmEmitterContext context, Operand op, Operand shift, bool round, int eSize) + { + if (round) + { + Operand lblEnd = Label(); + + Operand eSizeOp = Const(eSize); + Operand zeroL = Const(0L); + Operand one = Const(1); + Operand oneL = Const(1L); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), zeroL); + + context.BranchIf(lblEnd, shift, eSizeOp, Comparison.GreaterOrEqual); + Operand roundConst = context.ShiftLeft(oneL, context.Subtract(shift, one)); + Operand add = context.Add(op, roundConst); + Operand sar = context.ShiftRightSI(add, shift); + if (eSize == 64) + { + Operand shr = context.ShiftRightUI(add, shift); + Operand left = context.BitwiseAnd(context.Negate(op), context.BitwiseExclusiveOr(op, add)); + Operand isLess = context.ICompareLess(left, zeroL); + context.Copy(res, context.ConditionalSelect(isLess, shr, sar)); + } + else + { + context.Copy(res, sar); + } + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + else + { + Operand lblEnd = Label(); + + Operand eSizeOp = Const(eSize); + Operand zeroL = Const(0L); + Operand negOneL = Const(-1L); + + Operand sar = context.ShiftRightSI(op, shift); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), sar); + + context.BranchIf(lblEnd, shift, eSizeOp, Comparison.Less); + Operand isLess = context.ICompareLess(op, zeroL); + context.Copy(res, context.ConditionalSelect(isLess, negOneL, zeroL)); + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + } + + // shift := [1, 128]; eSize := {8, 16, 32, 64}. + // ulong UnsignedShrReg(ulong op, int shift, bool round, int eSize); + private static Operand EmitUnsignedShrReg(ArmEmitterContext context, Operand op, Operand shift, bool round, int eSize) + { + if (round) + { + Operand lblEnd = Label(); + + Operand zeroUL = Const(0UL); + Operand one = Const(1); + Operand oneUL = Const(1UL); + Operand eSizeMaxOp = Const(64); + Operand oneShl63UL = Const(1UL << 63); + + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), zeroUL); + + context.BranchIf(lblEnd, shift, eSizeMaxOp, Comparison.Greater); + Operand roundConst = context.ShiftLeft(oneUL, context.Subtract(shift, one)); + Operand add = context.Add(op, roundConst); + Operand shr = context.ShiftRightUI(add, shift); + Operand isEqual = context.ICompareEqual(shift, eSizeMaxOp); + context.Copy(res, context.ConditionalSelect(isEqual, zeroUL, shr)); + if (eSize == 64) + { + context.BranchIf(lblEnd, add, op, Comparison.GreaterOrEqualUI); + Operand right = context.BitwiseOr(shr, context.ShiftRightUI(oneShl63UL, context.Subtract(shift, one))); + context.Copy(res, context.ConditionalSelect(isEqual, oneUL, right)); + } + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + else + { + Operand lblEnd = Label(); + + Operand eSizeOp = Const(eSize); + Operand zeroUL = Const(0UL); + + Operand shr = context.ShiftRightUI(op, shift); + Operand res = context.Copy(context.AllocateLocal(OperandType.I64), shr); + + context.BranchIf(lblEnd, shift, eSizeOp, Comparison.Less); + context.Copy(res, zeroUL); + context.Branch(lblEnd); + + context.MarkLabel(lblEnd); + + return res; + } + } } } diff --git a/ARMeilleure/Instructions/SoftFallback.cs b/ARMeilleure/Instructions/SoftFallback.cs index 829dd37a3..d5e1ab65d 100644 --- a/ARMeilleure/Instructions/SoftFallback.cs +++ b/ARMeilleure/Instructions/SoftFallback.cs @@ -5,287 +5,6 @@ namespace ARMeilleure.Instructions { static class SoftFallback { -#region "ShlReg" - public static long SignedShlReg(long value, long shift, bool round, int size) - { - int eSize = 8 << size; - - int shiftLsB = (sbyte)shift; - - if (shiftLsB < 0) - { - return SignedShrReg(value, -shiftLsB, round, eSize); - } - else if (shiftLsB > 0) - { - if (shiftLsB >= eSize) - { - return 0L; - } - - return value << shiftLsB; - } - else /* if (shiftLsB == 0) */ - { - return value; - } - } - - public static ulong UnsignedShlReg(ulong value, ulong shift, bool round, int size) - { - int eSize = 8 << size; - - int shiftLsB = (sbyte)shift; - - if (shiftLsB < 0) - { - return UnsignedShrReg(value, -shiftLsB, round, eSize); - } - else if (shiftLsB > 0) - { - if (shiftLsB >= eSize) - { - return 0UL; - } - - return value << shiftLsB; - } - else /* if (shiftLsB == 0) */ - { - return value; - } - } - - public static long SignedShlRegSatQ(long value, long shift, bool round, int size) - { - ExecutionContext context = NativeInterface.GetContext(); - - int eSize = 8 << size; - - int shiftLsB = (sbyte)shift; - - if (shiftLsB < 0) - { - return SignedShrReg(value, -shiftLsB, round, eSize); - } - else if (shiftLsB > 0) - { - if (shiftLsB >= eSize) - { - return SignedSignSatQ(value, eSize, context); - } - - if (eSize == 64) - { - long shl = value << shiftLsB; - long shr = shl >> shiftLsB; - - if (shr != value) - { - return SignedSignSatQ(value, eSize, context); - } - else /* if (shr == value) */ - { - return shl; - } - } - else /* if (eSize != 64) */ - { - return SignedSrcSignedDstSatQ(value << shiftLsB, size); // InstEmitSimdHelper.EmitSignedSrcSatQ(signedDst: true). - } - } - else /* if (shiftLsB == 0) */ - { - return value; - } - } - - public static ulong UnsignedShlRegSatQ(ulong value, ulong shift, bool round, int size) - { - ExecutionContext context = NativeInterface.GetContext(); - - int eSize = 8 << size; - - int shiftLsB = (sbyte)shift; - - if (shiftLsB < 0) - { - return UnsignedShrReg(value, -shiftLsB, round, eSize); - } - else if (shiftLsB > 0) - { - if (shiftLsB >= eSize) - { - return UnsignedSignSatQ(value, eSize, context); - } - - if (eSize == 64) - { - ulong shl = value << shiftLsB; - ulong shr = shl >> shiftLsB; - - if (shr != value) - { - return UnsignedSignSatQ(value, eSize, context); - } - else /* if (shr == value) */ - { - return shl; - } - } - else /* if (eSize != 64) */ - { - return UnsignedSrcUnsignedDstSatQ(value << shiftLsB, size); // InstEmitSimdHelper.EmitUnsignedSrcSatQ(signedDst: false). - } - } - else /* if (shiftLsB == 0) */ - { - return value; - } - } - - private static long SignedShrReg(long value, int shift, bool round, int eSize) // shift := [1, 128]; eSize := {8, 16, 32, 64}. - { - if (round) - { - if (shift >= eSize) - { - return 0L; - } - - long roundConst = 1L << (shift - 1); - - long add = value + roundConst; - - if (eSize == 64) - { - if ((~value & (value ^ add)) < 0L) - { - return (long)((ulong)add >> shift); - } - else - { - return add >> shift; - } - } - else /* if (eSize != 64) */ - { - return add >> shift; - } - } - else /* if (!round) */ - { - if (shift >= eSize) - { - if (value < 0L) - { - return -1L; - } - else /* if (value >= 0L) */ - { - return 0L; - } - } - - return value >> shift; - } - } - - private static ulong UnsignedShrReg(ulong value, int shift, bool round, int eSize) // shift := [1, 128]; eSize := {8, 16, 32, 64}. - { - if (round) - { - if (shift > 64) - { - return 0UL; - } - - ulong roundConst = 1UL << (shift - 1); - - ulong add = value + roundConst; - - if (eSize == 64) - { - if ((add < value) && (add < roundConst)) - { - if (shift == 64) - { - return 1UL; - } - - return (add >> shift) | (0x8000000000000000UL >> (shift - 1)); - } - else - { - if (shift == 64) - { - return 0UL; - } - - return add >> shift; - } - } - else /* if (eSize != 64) */ - { - if (shift == 64) - { - return 0UL; - } - - return add >> shift; - } - } - else /* if (!round) */ - { - if (shift >= eSize) - { - return 0UL; - } - - return value >> shift; - } - } - - private static long SignedSignSatQ(long op, int eSize, ExecutionContext context) // eSize := {8, 16, 32, 64}. - { - long tMaxValue = (1L << (eSize - 1)) - 1L; - long tMinValue = -(1L << (eSize - 1)); - - if (op > 0L) - { - context.Fpsr |= FPSR.Qc; - - return tMaxValue; - } - else if (op < 0L) - { - context.Fpsr |= FPSR.Qc; - - return tMinValue; - } - else - { - return 0L; - } - } - - private static ulong UnsignedSignSatQ(ulong op, int eSize, ExecutionContext context) // eSize := {8, 16, 32, 64}. - { - ulong tMaxValue = ulong.MaxValue >> (64 - eSize); - - if (op > 0UL) - { - context.Fpsr |= FPSR.Qc; - - return tMaxValue; - } - else - { - return 0UL; - } - } -#endregion - #region "ShrImm64" public static long SignedShrImm64(long value, long roundConst, int shift) { @@ -508,55 +227,6 @@ namespace ARMeilleure.Instructions } #endregion -#region "Saturating" - private static long SignedSrcSignedDstSatQ(long op, int size) - { - ExecutionContext context = NativeInterface.GetContext(); - - int eSize = 8 << size; - - long tMaxValue = (1L << (eSize - 1)) - 1L; - long tMinValue = -(1L << (eSize - 1)); - - if (op > tMaxValue) - { - context.Fpsr |= FPSR.Qc; - - return tMaxValue; - } - else if (op < tMinValue) - { - context.Fpsr |= FPSR.Qc; - - return tMinValue; - } - else - { - return op; - } - } - - private static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size) - { - ExecutionContext context = NativeInterface.GetContext(); - - int eSize = 8 << size; - - ulong tMaxValue = (1UL << eSize) - 1UL; - - if (op > tMaxValue) - { - context.Fpsr |= FPSR.Qc; - - return tMaxValue; - } - else - { - return op; - } - } -#endregion - #region "Count" public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). { diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs index b36472b83..0da69ebc3 100644 --- a/ARMeilleure/Translation/Delegates.cs +++ b/ARMeilleure/Translation/Delegates.cs @@ -179,8 +179,6 @@ namespace ARMeilleure.Translation SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2))); @@ -190,8 +188,6 @@ namespace ARMeilleure.Translation SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ))); SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64))); SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert))); diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 71f69ef7c..01f089882 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 3695; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 3700; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From 6f0395538b8e8af3bba7536b44780d57e51e8697 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 19 Sep 2022 18:05:26 +0000 Subject: [PATCH 3/8] Avalonia - Use embedded window for avalonia (#3674) * wip * use embedded window * fix race condition on opengl Windows * fix glx issues on prime nvidia * fix mouse support win32 * clean up * addressed review * addressed review * fix warnings * fix sotware keyboard dialog * Update Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs Co-authored-by: gdkchan * remove double semi Co-authored-by: gdkchan --- Ryujinx.Ava/AppHost.cs | 92 ++-- Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 15 +- Ryujinx.Ava/Program.cs | 50 +- .../Ui/Applet/SwkbdAppletDialog.axaml.cs | 69 ++- Ryujinx.Ava/Ui/Backend/BackendSurface.cs | 76 --- Ryujinx.Ava/Ui/Backend/Interop.cs | 49 -- Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs | 26 - .../Ui/Backend/Vulkan/ResultExtensions.cs | 17 - .../Backend/Vulkan/Skia/VulkanRenderTarget.cs | 201 -------- .../Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs | 61 --- .../Ui/Backend/Vulkan/Skia/VulkanSurface.cs | 53 -- .../Vulkan/Surfaces/IVulkanPlatformSurface.cs | 13 - .../Surfaces/VulkanSurfaceRenderTarget.cs | 134 ----- .../Backend/Vulkan/VulkanCommandBufferPool.cs | 215 --------- Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs | 63 --- .../Ui/Backend/Vulkan/VulkanDisplay.cs | 456 ------------------ Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs | 165 ------- .../Ui/Backend/Vulkan/VulkanInstance.cs | 135 ------ .../Ui/Backend/Vulkan/VulkanMemoryHelper.cs | 59 --- .../Ui/Backend/Vulkan/VulkanOptions.cs | 44 -- .../Ui/Backend/Vulkan/VulkanPhysicalDevice.cs | 219 --------- .../Backend/Vulkan/VulkanPlatformInterface.cs | 70 --- Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs | 18 - .../Ui/Backend/Vulkan/VulkanSemaphorePair.cs | 32 -- .../Ui/Backend/Vulkan/VulkanSurface.cs | 75 --- .../Vulkan/VulkanSurfaceRenderingSession.cs | 43 -- .../Ui/Controls/ContentDialogHelper.cs | 78 ++- Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs | 204 ++++++++ .../Ui/Controls/OpenGLEmbeddedWindow.cs | 85 ++++ .../Ui/Controls/OpenGLRendererControl.cs | 192 -------- Ryujinx.Ava/Ui/Controls/RendererControl.cs | 96 ---- Ryujinx.Ava/Ui/Controls/RendererHost.axaml | 14 + Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs | 126 +++++ .../Ui/Controls/VulkanEmbeddedWindow.cs | 33 ++ .../Ui/Controls/VulkanRendererControl.cs | 220 --------- Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs | 113 +++++ .../Ui/ViewModels/SettingsViewModel.cs | 64 +-- .../Windows/ContentDialogOverlayWindow.axaml | 27 ++ .../ContentDialogOverlayWindow.axaml.cs | 25 + Ryujinx.Ava/Ui/Windows/MainWindow.axaml | 1 + Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 25 +- .../Ui/Windows/SettingsWindow.axaml.cs | 12 +- Ryujinx.Graphics.GAL/IWindow.cs | 2 +- .../Commands/Window/WindowPresentCommand.cs | 4 +- .../Multithreading/ThreadedWindow.cs | 4 +- Ryujinx.Graphics.Gpu/Window.cs | 2 +- Ryujinx.Graphics.OpenGL/Window.cs | 61 +-- Ryujinx.Graphics.Vulkan/ImageWindow.cs | 429 ---------------- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 66 +-- Ryujinx.Graphics.Vulkan/Window.cs | 2 +- Ryujinx.Graphics.Vulkan/WindowBase.cs | 2 +- Ryujinx.HLE/Switch.cs | 2 +- Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs | 24 +- Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs | 2 +- Ryujinx.Headless.SDL2/WindowBase.cs | 4 +- Ryujinx/Ui/GLRenderer.cs | 24 +- Ryujinx/Ui/RendererWidgetBase.cs | 4 +- Ryujinx/Ui/VKRenderer.cs | 2 +- 58 files changed, 868 insertions(+), 3531 deletions(-) delete mode 100644 Ryujinx.Ava/Ui/Backend/BackendSurface.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Interop.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs delete mode 100644 Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs create mode 100644 Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs create mode 100644 Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs delete mode 100644 Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs delete mode 100644 Ryujinx.Ava/Ui/Controls/RendererControl.cs create mode 100644 Ryujinx.Ava/Ui/Controls/RendererHost.axaml create mode 100644 Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs create mode 100644 Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs delete mode 100644 Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs create mode 100644 Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs create mode 100644 Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml create mode 100644 Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs delete mode 100644 Ryujinx.Graphics.Vulkan/ImageWindow.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 7e3cddc88..7cf5934a6 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -1,6 +1,5 @@ using ARMeilleure.Translation; using ARMeilleure.Translation.PTC; -using Avalonia; using Avalonia.Input; using Avalonia.Threading; using LibHac.Tools.FsSystem; @@ -12,10 +11,8 @@ using Ryujinx.Audio.Integration; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; -using Ryujinx.Ava.Ui.Backend.Vulkan; using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Models; -using Ryujinx.Ava.Ui.Vulkan; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -39,6 +36,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SPB.Graphics.Vulkan; using System; using System.Diagnostics; using System.IO; @@ -58,24 +56,24 @@ namespace Ryujinx.Ava { private const int CursorHideIdleTime = 8; // Hide Cursor seconds private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. + private const int TargetFps = 60; - private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); + private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); + private readonly long _ticksPerFrame; + private readonly Stopwatch _chrono; private readonly AccountManager _accountManager; private readonly UserChannelPersistence _userChannelPersistence; - private readonly InputManager _inputManager; - - private readonly IKeyboard _keyboardInterface; - private readonly MainWindow _parent; - + private readonly IKeyboard _keyboardInterface; private readonly GraphicsDebugLevel _glLogLevel; private bool _hideCursorOnIdle; private bool _isStopped; private bool _isActive; private long _lastCursorMoveTime; + private long _ticks = 0; private KeyboardHotkeyState _prevHotkeyState; @@ -93,7 +91,7 @@ namespace Ryujinx.Ava public event EventHandler AppExit; public event EventHandler StatusUpdatedEvent; - public RendererControl Renderer { get; } + public RendererHost Renderer { get; } public VirtualFileSystem VirtualFileSystem { get; } public ContentManager ContentManager { get; } public Switch Device { get; set; } @@ -111,7 +109,7 @@ namespace Ryujinx.Ava private object _lockObject = new(); public AppHost( - RendererControl renderer, + RendererHost renderer, InputManager inputManager, string applicationPath, VirtualFileSystem virtualFileSystem, @@ -128,7 +126,7 @@ namespace Ryujinx.Ava _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _lastCursorMoveTime = Stopwatch.GetTimestamp(); _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _inputManager.SetMouseDriver(new AvaloniaMouseDriver(renderer)); + _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_parent, renderer)); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); NpadManager = _inputManager.CreateNpadManager(); @@ -138,6 +136,9 @@ namespace Ryujinx.Ava VirtualFileSystem = virtualFileSystem; ContentManager = contentManager; + _chrono = new Stopwatch(); + _ticksPerFrame = Stopwatch.Frequency / TargetFps; + if (ApplicationPath.StartsWith("@SystemContent")) { ApplicationPath = _parent.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); @@ -177,7 +178,7 @@ namespace Ryujinx.Ava if (_renderer != null) { double scale = _parent.PlatformImpl.RenderScaling; - _renderer.Window.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); + _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); } } @@ -335,8 +336,6 @@ namespace Ryujinx.Ava return; } - AvaloniaLocator.Current.GetService()?.MainSurface.Display.ChangeVSyncMode(true); - _isStopped = true; _isActive = false; } @@ -376,6 +375,8 @@ namespace Ryujinx.Ava _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); + + _chrono.Stop(); } public void DisposeGpu() @@ -389,8 +390,7 @@ namespace Ryujinx.Ava Renderer?.MakeCurrent(); Device.DisposeGpu(); - - Renderer?.DestroyBackgroundContext(); + Renderer?.MakeCurrent(null); } @@ -596,16 +596,11 @@ namespace Ryujinx.Ava IRenderer renderer; - if (Program.UseVulkan) + if (Renderer.IsVulkan) { - var vulkan = AvaloniaLocator.Current.GetService(); + string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - renderer = new VulkanRenderer(vulkan.Instance.InternalHandle, - vulkan.MainSurface.Device.InternalHandle, - vulkan.PhysicalDevice.InternalHandle, - vulkan.MainSurface.Device.Queue.InternalHandle, - vulkan.PhysicalDevice.QueueFamilyIndex, - vulkan.MainSurface.Device.Lock); + renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); } else { @@ -778,11 +773,7 @@ namespace Ryujinx.Ava { Width = (int)e.Width; Height = (int)e.Height; - - if (!Program.UseVulkan) - { - SetRendererWindowSize(e); - } + SetRendererWindowSize(e); } private void MainLoop() @@ -822,12 +813,10 @@ namespace Ryujinx.Ava _renderer.ScreenCaptured += Renderer_ScreenCaptured; - (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext((Renderer as OpenGLRendererControl).GameContext)); + (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Renderer.GetContext())); Renderer.MakeCurrent(); - AvaloniaLocator.Current.GetService()?.MainSurface?.Display?.ChangeVSyncMode(Device.EnableDeviceVsync); - Device.Gpu.Renderer.Initialize(_glLogLevel); Width = (int)Renderer.Bounds.Width; @@ -835,16 +824,20 @@ namespace Ryujinx.Ava _renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling)); + _chrono.Start(); + Device.Gpu.Renderer.RunLoop(() => { Device.Gpu.SetGpuThread(); Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); - Renderer.Start(); - while (_isActive) { + _ticks += _chrono.ElapsedTicks; + + _chrono.Restart(); + if (Device.WaitFifo()) { Device.Statistics.RecordFifoStart(); @@ -860,19 +853,20 @@ namespace Ryujinx.Ava _parent.SwitchToGameControl(); } - Device.PresentFrame(Present); + Device.PresentFrame(() => Renderer?.SwapBuffers()); + } + + if (_ticks >= _ticksPerFrame) + { + UpdateStatus(); } } - - Renderer.Stop(); }); Renderer?.MakeCurrent(null); - - Renderer.SizeChanged -= Window_SizeChanged; } - private void Present(object image) + public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"]; @@ -886,24 +880,12 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, Device.GetVolume(), - Program.UseVulkan ? "Vulkan" : "OpenGL", + Renderer.IsVulkan ? "Vulkan" : "OpenGL", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", $"GPU: {_renderer.GetHardwareInfo().GpuVendor}")); - - if (Program.UseVulkan) - { - var platformInterface = AvaloniaLocator.Current.GetService(); - if (platformInterface.MainSurface.Display.IsSurfaceChanged()) - { - SetRendererWindowSize(new Size(Width, Height)); - return; - } - } - - Renderer.Present(image); } public async Task ShowExitPrompt() @@ -985,8 +967,6 @@ namespace Ryujinx.Ava case KeyboardHotkeyState.ToggleVSync: Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - AvaloniaLocator.Current.GetService()?.MainSurface?.Display?.ChangeVSyncMode(Device.EnableDeviceVsync); - break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; diff --git a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index 74c435b5c..9ad0310a5 100644 --- a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -14,20 +14,27 @@ namespace Ryujinx.Ava.Input private Control _widget; private bool _isDisposed; private Size _size; + private readonly Window _window; public bool[] PressedButtons { get; } public Vector2 CurrentPosition { get; private set; } public Vector2 Scroll { get; private set; } - public AvaloniaMouseDriver(Control parent) + public AvaloniaMouseDriver(Window window, Control parent) { _widget = parent; + _window = window; _widget.PointerMoved += Parent_PointerMovedEvent; _widget.PointerPressed += Parent_PointerPressEvent; _widget.PointerReleased += Parent_PointerReleaseEvent; _widget.PointerWheelChanged += Parent_ScrollEvent; + + _window.PointerMoved += Parent_PointerMovedEvent; + _window.PointerPressed += Parent_PointerPressEvent; + _window.PointerReleased += Parent_PointerReleaseEvent; + _window.PointerWheelChanged += Parent_ScrollEvent; PressedButtons = new bool[(int)MouseButton.Count]; @@ -47,7 +54,6 @@ namespace Ryujinx.Ava.Input private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) { - var pointerProperties = args.GetCurrentPoint(_widget).Properties; PressedButtons[(int)args.InitialPressMouseButton - 1] = false; } @@ -125,6 +131,11 @@ namespace Ryujinx.Ava.Input _widget.PointerReleased -= Parent_PointerReleaseEvent; _widget.PointerWheelChanged -= Parent_ScrollEvent; + _window.PointerMoved -= Parent_PointerMovedEvent; + _window.PointerPressed -= Parent_PointerPressEvent; + _window.PointerReleased -= Parent_PointerReleaseEvent; + _window.PointerWheelChanged -= Parent_ScrollEvent; + _widget = null; } } diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 242246ebe..61b184c61 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,9 +1,7 @@ using ARMeilleure.Translation.PTC; using Avalonia; -using Avalonia.OpenGL; using Avalonia.Rendering; using Avalonia.Threading; -using Ryujinx.Ava.Ui.Backend; using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common; @@ -12,12 +10,10 @@ using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.System; using Ryujinx.Common.SystemInfo; -using Ryujinx.Graphics.Vulkan; using Ryujinx.Modules; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using System; -using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -34,7 +30,6 @@ namespace Ryujinx.Ava public static bool PreviewerDetached { get; private set; } public static RenderTimer RenderTimer { get; private set; } - public static bool UseVulkan { get; private set; } [DllImport("user32.dll", SetLastError = true)] public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type); @@ -71,36 +66,16 @@ namespace Ryujinx.Ava EnableMultiTouch = true, EnableIme = true, UseEGL = false, - UseGpu = !UseVulkan, - GlProfiles = new List() - { - new GlVersion(GlProfileType.OpenGL, 4, 3) - } + UseGpu = false }) .With(new Win32PlatformOptions { EnableMultitouch = true, - UseWgl = !UseVulkan, - WglProfiles = new List() - { - new GlVersion(GlProfileType.OpenGL, 4, 3) - }, + UseWgl = false, AllowEglInitialization = false, CompositionBackdropCornerRadius = 8f, }) .UseSkia() - .With(new Ui.Vulkan.VulkanOptions() - { - ApplicationName = "Ryujinx.Graphics.Vulkan", - MaxQueueCount = 2, - PreferDiscreteGpu = true, - PreferredDevice = !PreviewerDetached ? "" : ConfigurationState.Instance.Graphics.PreferredGpu.Value, - UseDebug = !PreviewerDetached ? false : ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value != GraphicsDebugLevel.None, - }) - .With(new SkiaOptions() - { - CustomGpuFactory = UseVulkan ? SkiaGpuFactory.CreateVulkanGpu : null - }) .AfterSetup(_ => { AvaloniaLocator.CurrentMutable @@ -176,26 +151,7 @@ namespace Ryujinx.Ava ReloadConfig(); - UseVulkan = PreviewerDetached ? ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan : false; - - if (UseVulkan) - { - if (VulkanRenderer.GetPhysicalDevices().Length == 0) - { - UseVulkan = false; - - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; - - Logger.Warning?.PrintMsg(LogClass.Application, "A suitable Vulkan physical device is not available. Falling back to OpenGL"); - } - } - - if (UseVulkan) - { - // With a custom gpu backend, avalonia doesn't enable dpi awareness, so the backend must handle it. This isn't so for the opengl backed, - // as that uses avalonia's gpu backend and it's enabled there. - ForceDpiAware.Windows(); - } + ForceDpiAware.Windows(); WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); ActualScaleFactor = ForceDpiAware.GetActualScaleFactor() / BaseDpi; diff --git a/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs index 564ee4b29..e4ddba966 100644 --- a/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs +++ b/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; @@ -59,29 +60,63 @@ namespace Ryujinx.Ava.Ui.Controls string input = string.Empty; + var overlay = new ContentDialogOverlayWindow() + { + Height = window.Bounds.Height, + Width = window.Bounds.Width, + Position = window.PointToScreen(new Point()) + }; + + window.PositionChanged += OverlayOnPositionChanged; + + void OverlayOnPositionChanged(object sender, PixelPointEventArgs e) + { + overlay.Position = window.PointToScreen(new Point()); + } + + contentDialog = overlay.ContentDialog; + + bool opened = false; + content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); - if (contentDialog != null) + content._host = contentDialog; + contentDialog.Title = title; + contentDialog.PrimaryButtonText = args.SubmitText; + contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length); + contentDialog.SecondaryButtonText = ""; + contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"]; + contentDialog.Content = content; + + TypedEventHandler handler = (sender, eventArgs) => { - content._host = contentDialog; - contentDialog.Title = title; - contentDialog.PrimaryButtonText = args.SubmitText; - contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length); - contentDialog.SecondaryButtonText = ""; - contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"]; - contentDialog.Content = content; - TypedEventHandler handler = (sender, eventArgs) => + if (eventArgs.Result == ContentDialogResult.Primary) { - if (eventArgs.Result == ContentDialogResult.Primary) - { - result = UserResult.Ok; - input = content.Input.Text; - } - }; - contentDialog.Closed += handler; + result = UserResult.Ok; + input = content.Input.Text; + } + }; + contentDialog.Closed += handler; + + overlay.Opened += OverlayOnActivated; + + async void OverlayOnActivated(object sender, EventArgs e) + { + if (opened) + { + return; + } + + opened = true; + + overlay.Position = window.PointToScreen(new Point()); + await contentDialog.ShowAsync(); contentDialog.Closed -= handler; - } + overlay.Close(); + }; + + await overlay.ShowDialog(window); return (result, input); } diff --git a/Ryujinx.Ava/Ui/Backend/BackendSurface.cs b/Ryujinx.Ava/Ui/Backend/BackendSurface.cs deleted file mode 100644 index 423fe038e..000000000 --- a/Ryujinx.Ava/Ui/Backend/BackendSurface.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Avalonia; -using System; -using System.Runtime.InteropServices; -using static Ryujinx.Ava.Ui.Backend.Interop; - -namespace Ryujinx.Ava.Ui.Backend -{ - public abstract class BackendSurface : IDisposable - { - protected IntPtr Display => _display; - - private IntPtr _display = IntPtr.Zero; - - [DllImport("libX11.so.6")] - public static extern IntPtr XOpenDisplay(IntPtr display); - - [DllImport("libX11.so.6")] - public static extern int XCloseDisplay(IntPtr display); - - private PixelSize _currentSize; - public IntPtr Handle { get; protected set; } - - public bool IsDisposed { get; private set; } - - public BackendSurface(IntPtr handle) - { - Handle = handle; - - if (OperatingSystem.IsLinux()) - { - _display = XOpenDisplay(IntPtr.Zero); - } - } - - public PixelSize Size - { - get - { - PixelSize size = new PixelSize(); - if (OperatingSystem.IsWindows()) - { - GetClientRect(Handle, out var rect); - size = new PixelSize(rect.right, rect.bottom); - } - else if (OperatingSystem.IsLinux()) - { - XWindowAttributes attributes = new XWindowAttributes(); - XGetWindowAttributes(Display, Handle, ref attributes); - - size = new PixelSize(attributes.width, attributes.height); - } - - _currentSize = size; - - return size; - } - } - - public PixelSize CurrentSize => _currentSize; - - public virtual void Dispose() - { - if (IsDisposed) - { - throw new ObjectDisposedException(nameof(BackendSurface)); - } - - IsDisposed = true; - - if (_display != IntPtr.Zero) - { - XCloseDisplay(_display); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Backend/Interop.cs b/Ryujinx.Ava/Ui/Backend/Interop.cs deleted file mode 100644 index 617e97678..000000000 --- a/Ryujinx.Ava/Ui/Backend/Interop.cs +++ /dev/null @@ -1,49 +0,0 @@ -using FluentAvalonia.Interop; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Ava.Ui.Backend -{ - public static class Interop - { - [StructLayout(LayoutKind.Sequential)] - public struct XWindowAttributes - { - public int x; - public int y; - public int width; - public int height; - public int border_width; - public int depth; - public IntPtr visual; - public IntPtr root; - public int c_class; - public int bit_gravity; - public int win_gravity; - public int backing_store; - public IntPtr backing_planes; - public IntPtr backing_pixel; - public int save_under; - public IntPtr colormap; - public int map_installed; - public int map_state; - public IntPtr all_event_masks; - public IntPtr your_event_mask; - public IntPtr do_not_propagate_mask; - public int override_direct; - public IntPtr screen; - } - - [DllImport("user32.dll")] - public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect); - - [DllImport("libX11.so.6")] - public static extern int XCloseDisplay(IntPtr display); - - [DllImport("libX11.so.6")] - public static extern int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes); - - [DllImport("libX11.so.6")] - public static extern IntPtr XOpenDisplay(IntPtr display); - } -} diff --git a/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs b/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs deleted file mode 100644 index 335bc905f..000000000 --- a/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Avalonia; -using Avalonia.Skia; -using Ryujinx.Ava.Ui.Vulkan; -using Ryujinx.Ava.Ui.Backend.Vulkan; - -namespace Ryujinx.Ava.Ui.Backend -{ - public static class SkiaGpuFactory - { - public static ISkiaGpu CreateVulkanGpu() - { - var skiaOptions = AvaloniaLocator.Current.GetService() ?? new SkiaOptions(); - var platformInterface = AvaloniaLocator.Current.GetService(); - - if (platformInterface == null) - { - VulkanPlatformInterface.TryInitialize(); - } - - var gpu = new VulkanSkiaGpu(skiaOptions.MaxGpuResourceSizeBytes); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(gpu); - - return gpu; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs deleted file mode 100644 index 2a1cd2293..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - public static class ResultExtensions - { - public static void ThrowOnError(this Result result) - { - // Only negative result codes are errors. - if ((int)result < (int)Result.Success) - { - throw new Exception($"Unexpected API error \"{result}\"."); - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs deleted file mode 100644 index 70ec39c7c..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using Avalonia; -using Avalonia.Skia; -using Ryujinx.Ava.Ui.Vulkan; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Silk.NET.Vulkan; -using SkiaSharp; - -namespace Ryujinx.Ava.Ui.Backend.Vulkan -{ - internal class VulkanRenderTarget : ISkiaGpuRenderTarget - { - public GRContext GrContext { get; private set; } - - private readonly VulkanSurfaceRenderTarget _surface; - private readonly VulkanPlatformInterface _vulkanPlatformInterface; - private readonly IVulkanPlatformSurface _vulkanPlatformSurface; - private GRVkBackendContext _grVkBackend; - - public VulkanRenderTarget(VulkanPlatformInterface vulkanPlatformInterface, IVulkanPlatformSurface vulkanPlatformSurface) - { - _surface = vulkanPlatformInterface.CreateRenderTarget(vulkanPlatformSurface); - _vulkanPlatformInterface = vulkanPlatformInterface; - _vulkanPlatformSurface = vulkanPlatformSurface; - - Initialize(); - } - - private void Initialize() - { - GRVkGetProcedureAddressDelegate getProc = GetVulkanProcAddress; - - _grVkBackend = new GRVkBackendContext() - { - VkInstance = _surface.Device.Handle, - VkPhysicalDevice = _vulkanPlatformInterface.PhysicalDevice.Handle, - VkDevice = _surface.Device.Handle, - VkQueue = _surface.Device.Queue.Handle, - GraphicsQueueIndex = _vulkanPlatformInterface.PhysicalDevice.QueueFamilyIndex, - GetProcedureAddress = getProc - }; - - GrContext = GRContext.CreateVulkan(_grVkBackend); - - var gpu = AvaloniaLocator.Current.GetService(); - - if (gpu.MaxResourceBytes.HasValue) - { - GrContext.SetResourceCacheLimit(gpu.MaxResourceBytes.Value); - } - } - - private IntPtr GetVulkanProcAddress(string name, IntPtr instanceHandle, IntPtr deviceHandle) - { - IntPtr addr; - - if (deviceHandle != IntPtr.Zero) - { - addr = _vulkanPlatformInterface.Api.GetDeviceProcAddr(new Device(deviceHandle), name); - - if (addr != IntPtr.Zero) - { - return addr; - } - - addr = _vulkanPlatformInterface.Api.GetDeviceProcAddr(new Device(_surface.Device.Handle), name); - - if (addr != IntPtr.Zero) - { - return addr; - } - } - - addr = _vulkanPlatformInterface.Api.GetInstanceProcAddr(new Instance(_vulkanPlatformInterface.Instance.Handle), name); - - if (addr == IntPtr.Zero) - { - addr = _vulkanPlatformInterface.Api.GetInstanceProcAddr(new Instance(instanceHandle), name); - } - - return addr; - } - - public void Dispose() - { - _grVkBackend.Dispose(); - GrContext.Dispose(); - _surface.Dispose(); - } - - public ISkiaGpuRenderSession BeginRenderingSession() - { - var session = _surface.BeginDraw(_vulkanPlatformSurface.Scaling); - bool success = false; - try - { - var disp = session.Display; - var api = session.Api; - - var size = session.Size; - var scaling = session.Scaling; - if (size.Width <= 0 || size.Height <= 0 || scaling < 0) - { - size = new Avalonia.PixelSize(1, 1); - scaling = 1; - } - - lock (GrContext) - { - GrContext.ResetContext(); - - var image = _surface.GetImage(); - - var imageInfo = new GRVkImageInfo() - { - CurrentQueueFamily = disp.QueueFamilyIndex, - Format = (uint)image.Format, - Image = image.Handle, - ImageLayout = (uint)image.CurrentLayout, - ImageTiling = (uint)image.Tiling, - ImageUsageFlags = _surface.UsageFlags, - LevelCount = _surface.MipLevels, - SampleCount = 1, - Protected = false, - Alloc = new GRVkAlloc() - { - Memory = image.MemoryHandle, - Flags = 0, - Offset = 0, - Size = _surface.MemorySize - } - }; - - var renderTarget = - new GRBackendRenderTarget((int)size.Width, (int)size.Height, 1, - imageInfo); - var surface = SKSurface.Create(GrContext, renderTarget, - GRSurfaceOrigin.TopLeft, - _surface.IsRgba ? SKColorType.Rgba8888 : SKColorType.Bgra8888, SKColorSpace.CreateSrgb()); - - if (surface == null) - { - throw new InvalidOperationException( - "Surface can't be created with the provided render target"); - } - - success = true; - - return new VulkanGpuSession(GrContext, renderTarget, surface, session); - } - } - finally - { - if (!success) - { - session.Dispose(); - } - } - } - - public bool IsCorrupted { get; } - - internal class VulkanGpuSession : ISkiaGpuRenderSession - { - private readonly GRBackendRenderTarget _backendRenderTarget; - private readonly VulkanSurfaceRenderingSession _vulkanSession; - - public VulkanGpuSession(GRContext grContext, - GRBackendRenderTarget backendRenderTarget, - SKSurface surface, - VulkanSurfaceRenderingSession vulkanSession) - { - GrContext = grContext; - _backendRenderTarget = backendRenderTarget; - SkSurface = surface; - _vulkanSession = vulkanSession; - - SurfaceOrigin = GRSurfaceOrigin.TopLeft; - } - - public void Dispose() - { - lock (_vulkanSession.Display.Lock) - { - SkSurface.Canvas.Flush(); - - SkSurface.Dispose(); - _backendRenderTarget.Dispose(); - GrContext.Flush(); - - _vulkanSession.Dispose(); - } - } - - public GRContext GrContext { get; } - public SKSurface SkSurface { get; } - public double ScaleFactor => _vulkanSession.Scaling; - public GRSurfaceOrigin SurfaceOrigin { get; } - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs deleted file mode 100644 index a5c270863..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia; -using Avalonia.Platform; -using Avalonia.Skia; -using Avalonia.X11; -using Ryujinx.Ava.Ui.Vulkan; -using Silk.NET.Vulkan; -using SkiaSharp; - -namespace Ryujinx.Ava.Ui.Backend.Vulkan -{ - public class VulkanSkiaGpu : ISkiaGpu - { - private readonly VulkanPlatformInterface _vulkan; - public long? MaxResourceBytes { get; } - - public VulkanSkiaGpu(long? maxResourceBytes) - { - _vulkan = AvaloniaLocator.Current.GetService(); - MaxResourceBytes = maxResourceBytes; - } - - public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable surfaces) - { - foreach (var surface in surfaces) - { - VulkanWindowSurface window; - - if (surface is IPlatformHandle handle) - { - window = new VulkanWindowSurface(handle.Handle); - } - else if (surface is X11FramebufferSurface x11FramebufferSurface) - { - // As of Avalonia 0.10.13, an IPlatformHandle isn't passed for linux, so use reflection to otherwise get the window id - var xId = (IntPtr)x11FramebufferSurface.GetType().GetField( - "_xid", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(x11FramebufferSurface); - - window = new VulkanWindowSurface(xId); - } - else - { - continue; - } - - VulkanRenderTarget vulkanRenderTarget = new VulkanRenderTarget(_vulkan, window); - - return vulkanRenderTarget; - } - - return null; - } - - public ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session) - { - return null; - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs deleted file mode 100644 index fd2d379b1..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Avalonia; -using Ryujinx.Ava.Ui.Vulkan; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.KHR; -using System; - -namespace Ryujinx.Ava.Ui.Backend.Vulkan -{ - internal class VulkanWindowSurface : BackendSurface, IVulkanPlatformSurface - { - public float Scaling => (float)Program.ActualScaleFactor; - - public PixelSize SurfaceSize => Size; - - public VulkanWindowSurface(IntPtr handle) : base(handle) - { - } - - public unsafe SurfaceKHR CreateSurface(VulkanInstance instance) - { - if (OperatingSystem.IsWindows()) - { - if (instance.Api.TryGetInstanceExtension(new Instance(instance.Handle), out KhrWin32Surface surfaceExtension)) - { - var createInfo = new Win32SurfaceCreateInfoKHR() { Hinstance = 0, Hwnd = Handle, SType = StructureType.Win32SurfaceCreateInfoKhr }; - - surfaceExtension.CreateWin32Surface(new Instance(instance.Handle), createInfo, null, out var surface).ThrowOnError(); - - return surface; - } - } - else if (OperatingSystem.IsLinux()) - { - if (instance.Api.TryGetInstanceExtension(new Instance(instance.Handle), out KhrXlibSurface surfaceExtension)) - { - var createInfo = new XlibSurfaceCreateInfoKHR() - { - SType = StructureType.XlibSurfaceCreateInfoKhr, - Dpy = (nint*)Display, - Window = Handle - }; - - surfaceExtension.CreateXlibSurface(new Instance(instance.Handle), createInfo, null, out var surface).ThrowOnError(); - - return surface; - } - } - - throw new PlatformNotSupportedException("The current platform does not support surface creation."); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs deleted file mode 100644 index 642d8a6a3..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using Avalonia; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan.Surfaces -{ - public interface IVulkanPlatformSurface : IDisposable - { - float Scaling { get; } - PixelSize SurfaceSize { get; } - SurfaceKHR CreateSurface(VulkanInstance instance); - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs deleted file mode 100644 index 510e6724b..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using Avalonia; -using Ryujinx.Graphics.Vulkan; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan.Surfaces -{ - internal class VulkanSurfaceRenderTarget : IDisposable - { - private readonly VulkanPlatformInterface _platformInterface; - private readonly Format _format; - - private VulkanCommandBufferPool.VulkanCommandBuffer _commandBuffer; - private VulkanImage Image { get; set; } - private object _lock = new object(); - - public uint MipLevels => Image.MipLevels; - public VulkanDevice Device { get; } - - public VulkanSurfaceRenderTarget(VulkanPlatformInterface platformInterface, VulkanSurface surface) - { - _platformInterface = platformInterface; - - var device = VulkanInitialization.CreateDevice(platformInterface.Api, - platformInterface.PhysicalDevice.InternalHandle, - platformInterface.PhysicalDevice.QueueFamilyIndex, - VulkanInitialization.GetSupportedExtensions(platformInterface.Api, platformInterface.PhysicalDevice.InternalHandle), - platformInterface.PhysicalDevice.QueueCount); - - Device = new VulkanDevice(device, platformInterface.PhysicalDevice, platformInterface.Api); - - Display = VulkanDisplay.CreateDisplay( - platformInterface.Instance, - Device, - platformInterface.PhysicalDevice, - surface); - Surface = surface; - - // Skia seems to only create surfaces from images with unorm format - IsRgba = Display.SurfaceFormat.Format >= Format.R8G8B8A8Unorm && - Display.SurfaceFormat.Format <= Format.R8G8B8A8Srgb; - - _format = IsRgba ? Format.R8G8B8A8Unorm : Format.B8G8R8A8Unorm; - } - - public bool IsRgba { get; } - - public uint ImageFormat => (uint)_format; - - public ulong MemorySize => Image.MemorySize; - - public VulkanDisplay Display { get; private set; } - - public VulkanSurface Surface { get; private set; } - - public uint UsageFlags => Image.UsageFlags; - - public PixelSize Size { get; private set; } - - public void Dispose() - { - lock (_lock) - { - DestroyImage(); - Display?.Dispose(); - Surface?.Dispose(); - Device?.Dispose(); - - Display = null; - Surface = null; - } - } - - public VulkanSurfaceRenderingSession BeginDraw(float scaling) - { - if (Image == null) - { - RecreateImage(); - } - - _commandBuffer?.WaitForFence(); - _commandBuffer = null; - - var session = new VulkanSurfaceRenderingSession(Display, Device, this, scaling); - - Image.TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr); - - return session; - } - - public void RecreateImage() - { - DestroyImage(); - CreateImage(); - } - - private void CreateImage() - { - Size = Display.Size; - - Image = new VulkanImage(Device, _platformInterface.PhysicalDevice, Display.CommandBufferPool, ImageFormat, Size); - } - - private void DestroyImage() - { - _commandBuffer?.WaitForFence(); - _commandBuffer = null; - Image?.Dispose(); - Image = null; - } - - public VulkanImage GetImage() - { - return Image; - } - - public void EndDraw() - { - lock (_lock) - { - if (Display == null) - { - return; - } - - _commandBuffer = Display.StartPresentation(); - - Display.BlitImageToCurrentImage(this, _commandBuffer.InternalHandle); - - Display.EndPresentation(_commandBuffer); - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs deleted file mode 100644 index a00ecf2b9..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanCommandBufferPool : IDisposable - { - private readonly VulkanDevice _device; - private readonly CommandPool _commandPool; - - private readonly List _usedCommandBuffers = new(); - private readonly object _lock = new object(); - - public unsafe VulkanCommandBufferPool(VulkanDevice device, VulkanPhysicalDevice physicalDevice) - { - _device = device; - - var commandPoolCreateInfo = new CommandPoolCreateInfo - { - SType = StructureType.CommandPoolCreateInfo, - Flags = CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit, - QueueFamilyIndex = physicalDevice.QueueFamilyIndex - }; - - device.Api.CreateCommandPool(_device.InternalHandle, commandPoolCreateInfo, null, out _commandPool) - .ThrowOnError(); - } - - private CommandBuffer AllocateCommandBuffer() - { - var commandBufferAllocateInfo = new CommandBufferAllocateInfo - { - SType = StructureType.CommandBufferAllocateInfo, - CommandPool = _commandPool, - CommandBufferCount = 1, - Level = CommandBufferLevel.Primary - }; - - lock (_lock) - { - _device.Api.AllocateCommandBuffers(_device.InternalHandle, commandBufferAllocateInfo, out var commandBuffer); - - return commandBuffer; - } - } - - public VulkanCommandBuffer CreateCommandBuffer() - { - return new(_device, this); - } - - public void FreeUsedCommandBuffers() - { - lock (_lock) - { - foreach (var usedCommandBuffer in _usedCommandBuffers) - { - usedCommandBuffer.Dispose(); - } - - _usedCommandBuffers.Clear(); - } - } - - private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer) - { - lock (_lock) - { - _usedCommandBuffers.Add(commandBuffer); - } - } - - public void Dispose() - { - lock (_lock) - { - FreeUsedCommandBuffers(); - _device.Api.DestroyCommandPool(_device.InternalHandle, _commandPool, Span.Empty); - } - } - - public class VulkanCommandBuffer : IDisposable - { - private readonly VulkanCommandBufferPool _commandBufferPool; - private readonly VulkanDevice _device; - private readonly Fence _fence; - private bool _hasEnded; - private bool _hasStarted; - private bool _isDisposed; - private object _lock = new object(); - - public IntPtr Handle => InternalHandle.Handle; - - internal CommandBuffer InternalHandle { get; } - - internal unsafe VulkanCommandBuffer(VulkanDevice device, VulkanCommandBufferPool commandBufferPool) - { - _device = device; - _commandBufferPool = commandBufferPool; - - InternalHandle = _commandBufferPool.AllocateCommandBuffer(); - - var fenceCreateInfo = new FenceCreateInfo() - { - SType = StructureType.FenceCreateInfo, - Flags = FenceCreateFlags.FenceCreateSignaledBit - }; - - device.Api.CreateFence(device.InternalHandle, fenceCreateInfo, null, out _fence); - } - - public void WaitForFence() - { - if (_isDisposed) - { - return; - } - - lock (_lock) - { - if (!_isDisposed) - { - _device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue); - } - } - } - - public void BeginRecording() - { - if (!_hasStarted) - { - _hasStarted = true; - - var beginInfo = new CommandBufferBeginInfo - { - SType = StructureType.CommandBufferBeginInfo, - Flags = CommandBufferUsageFlags.CommandBufferUsageOneTimeSubmitBit - }; - - _device.Api.BeginCommandBuffer(InternalHandle, beginInfo); - } - } - - public void EndRecording() - { - if (_hasStarted && !_hasEnded) - { - _hasEnded = true; - - _device.Api.EndCommandBuffer(InternalHandle); - } - } - - public void Submit() - { - Submit(null, null, null, _fence); - } - - public unsafe void Submit( - ReadOnlySpan waitSemaphores, - ReadOnlySpan waitDstStageMask, - ReadOnlySpan signalSemaphores, - Fence? fence = null) - { - EndRecording(); - - if (!fence.HasValue) - { - fence = _fence; - } - - fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores) - { - fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask) - { - var commandBuffer = InternalHandle; - var submitInfo = new SubmitInfo - { - SType = StructureType.SubmitInfo, - WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0, - PWaitSemaphores = pWaitSemaphores, - PWaitDstStageMask = pWaitDstStageMask, - CommandBufferCount = 1, - PCommandBuffers = &commandBuffer, - SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0, - PSignalSemaphores = pSignalSemaphores, - }; - - _device.Api.ResetFences(_device.InternalHandle, 1, fence.Value); - - _device.Submit(submitInfo, fence.Value); - } - } - - _commandBufferPool.DisposeCommandBuffer(this); - } - - public void Dispose() - { - lock (_lock) - { - if (!_isDisposed) - { - _isDisposed = true; - - _device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue); - _device.Api.FreeCommandBuffers(_device.InternalHandle, _commandBufferPool._commandPool, 1, InternalHandle); - _device.Api.DestroyFence(_device.InternalHandle, _fence, Span.Empty); - } - } - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs deleted file mode 100644 index 3d893e19a..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanDevice : IDisposable - { - private static object _lock = new object(); - - public VulkanDevice(Device apiHandle, VulkanPhysicalDevice physicalDevice, Vk api) - { - InternalHandle = apiHandle; - Api = api; - - api.GetDeviceQueue(apiHandle, physicalDevice.QueueFamilyIndex, 0, out var queue); - - Queue = new VulkanQueue(this, queue); - - PresentQueue = Queue; - } - - public IntPtr Handle => InternalHandle.Handle; - - internal Device InternalHandle { get; } - public Vk Api { get; } - - public VulkanQueue Queue { get; private set; } - public VulkanQueue PresentQueue { get; } - - public void Dispose() - { - WaitIdle(); - Queue = null; - Api.DestroyDevice(InternalHandle, Span.Empty); - } - - internal void Submit(SubmitInfo submitInfo, Fence fence = default) - { - lock (_lock) - { - Api.QueueSubmit(Queue.InternalHandle, 1, submitInfo, fence).ThrowOnError(); - } - } - - public void WaitIdle() - { - lock (_lock) - { - Api.DeviceWaitIdle(InternalHandle); - } - } - - public void QueueWaitIdle() - { - lock (_lock) - { - Api.QueueWaitIdle(Queue.InternalHandle); - } - } - - public object Lock => _lock; - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs deleted file mode 100644 index f3116fbda..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using Avalonia; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.KHR; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanDisplay : IDisposable - { - private static KhrSwapchain _swapchainExtension; - private readonly VulkanInstance _instance; - private readonly VulkanPhysicalDevice _physicalDevice; - private readonly VulkanSemaphorePair _semaphorePair; - private readonly VulkanDevice _device; - private uint _nextImage; - private readonly VulkanSurface _surface; - private SurfaceFormatKHR _surfaceFormat; - private SwapchainKHR _swapchain; - private Extent2D _swapchainExtent; - private Image[] _swapchainImages; - private ImageView[] _swapchainImageViews = Array.Empty(); - private bool _vsyncStateChanged; - private bool _vsyncEnabled; - private bool _surfaceChanged; - - public event EventHandler Presented; - - public VulkanCommandBufferPool CommandBufferPool { get; set; } - - public object Lock => _device.Lock; - - private VulkanDisplay(VulkanInstance instance, VulkanDevice device, - VulkanPhysicalDevice physicalDevice, VulkanSurface surface, SwapchainKHR swapchain, - Extent2D swapchainExtent) - { - _instance = instance; - _device = device; - _physicalDevice = physicalDevice; - _swapchain = swapchain; - _swapchainExtent = swapchainExtent; - _surface = surface; - - CreateSwapchainImages(); - - _semaphorePair = new VulkanSemaphorePair(_device); - - CommandBufferPool = new VulkanCommandBufferPool(device, physicalDevice); - } - - public PixelSize Size { get; private set; } - public uint QueueFamilyIndex => _physicalDevice.QueueFamilyIndex; - - internal SurfaceFormatKHR SurfaceFormat - { - get - { - if (_surfaceFormat.Format == Format.Undefined) - { - _surfaceFormat = _surface.GetSurfaceFormat(_physicalDevice); - } - - return _surfaceFormat; - } - } - - public void Dispose() - { - _device.WaitIdle(); - _semaphorePair?.Dispose(); - DestroyCurrentImageViews(); - _swapchainExtension.DestroySwapchain(_device.InternalHandle, _swapchain, Span.Empty); - CommandBufferPool.Dispose(); - } - - public bool IsSurfaceChanged() - { - var changed = _surfaceChanged; - _surfaceChanged = false; - - return changed; - } - - private static unsafe SwapchainKHR CreateSwapchain(VulkanInstance instance, VulkanDevice device, - VulkanPhysicalDevice physicalDevice, VulkanSurface surface, out Extent2D swapchainExtent, - SwapchainKHR? oldswapchain = null, bool vsyncEnabled = true) - { - if (_swapchainExtension == null) - { - instance.Api.TryGetDeviceExtension(instance.InternalHandle, device.InternalHandle, out _swapchainExtension); - } - - while (!surface.CanSurfacePresent(physicalDevice)) - { - Thread.Sleep(16); - } - - VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfaceCapabilities(physicalDevice.InternalHandle, - surface.ApiHandle, out var capabilities); - - var imageCount = capabilities.MinImageCount + 1; - if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount) - { - imageCount = capabilities.MaxImageCount; - } - - var surfaceFormat = surface.GetSurfaceFormat(physicalDevice); - - bool supportsIdentityTransform = capabilities.SupportedTransforms.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr); - bool isRotated = capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate90BitKhr) || - capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate270BitKhr); - - swapchainExtent = GetSwapchainExtent(surface, capabilities); - - CompositeAlphaFlagsKHR compositeAlphaFlags = GetSuitableCompositeAlphaFlags(capabilities); - - PresentModeKHR presentMode = GetSuitablePresentMode(physicalDevice, surface, vsyncEnabled); - - var swapchainCreateInfo = new SwapchainCreateInfoKHR - { - SType = StructureType.SwapchainCreateInfoKhr, - Surface = surface.ApiHandle, - MinImageCount = imageCount, - ImageFormat = surfaceFormat.Format, - ImageColorSpace = surfaceFormat.ColorSpace, - ImageExtent = swapchainExtent, - ImageUsage = - ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit, - ImageSharingMode = SharingMode.Exclusive, - ImageArrayLayers = 1, - PreTransform = supportsIdentityTransform && isRotated ? - SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr : - capabilities.CurrentTransform, - CompositeAlpha = compositeAlphaFlags, - PresentMode = presentMode, - Clipped = true, - OldSwapchain = oldswapchain ?? new SwapchainKHR() - }; - - _swapchainExtension.CreateSwapchain(device.InternalHandle, swapchainCreateInfo, null, out var swapchain) - .ThrowOnError(); - - if (oldswapchain != null) - { - _swapchainExtension.DestroySwapchain(device.InternalHandle, oldswapchain.Value, null); - } - - return swapchain; - } - - private static unsafe Extent2D GetSwapchainExtent(VulkanSurface surface, SurfaceCapabilitiesKHR capabilities) - { - Extent2D swapchainExtent; - if (capabilities.CurrentExtent.Width != uint.MaxValue) - { - swapchainExtent = capabilities.CurrentExtent; - } - else - { - var surfaceSize = surface.SurfaceSize; - - var width = Math.Clamp((uint)surfaceSize.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); - var height = Math.Clamp((uint)surfaceSize.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); - - swapchainExtent = new Extent2D(width, height); - } - - return swapchainExtent; - } - - private static unsafe CompositeAlphaFlagsKHR GetSuitableCompositeAlphaFlags(SurfaceCapabilitiesKHR capabilities) - { - var compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr; - - if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr)) - { - compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr; - } - else if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr)) - { - compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr; - } - - return compositeAlphaFlags; - } - - private static unsafe PresentModeKHR GetSuitablePresentMode(VulkanPhysicalDevice physicalDevice, VulkanSurface surface, bool vsyncEnabled) - { - uint presentModesCount; - - VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle, - surface.ApiHandle, - &presentModesCount, null); - - var presentModes = new PresentModeKHR[presentModesCount]; - - fixed (PresentModeKHR* pPresentModes = presentModes) - { - VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle, - surface.ApiHandle, &presentModesCount, pPresentModes); - } - - var modes = presentModes.ToList(); - - if (!vsyncEnabled && modes.Contains(PresentModeKHR.PresentModeImmediateKhr)) - { - return PresentModeKHR.PresentModeImmediateKhr; - } - else if (modes.Contains(PresentModeKHR.PresentModeMailboxKhr)) - { - return PresentModeKHR.PresentModeMailboxKhr; - } - else if (modes.Contains(PresentModeKHR.PresentModeFifoKhr)) - { - return PresentModeKHR.PresentModeFifoKhr; - } - else - { - return PresentModeKHR.PresentModeImmediateKhr; - } - } - - internal static VulkanDisplay CreateDisplay(VulkanInstance instance, VulkanDevice device, - VulkanPhysicalDevice physicalDevice, VulkanSurface surface) - { - var swapchain = CreateSwapchain(instance, device, physicalDevice, surface, out var extent, null, true); - - return new VulkanDisplay(instance, device, physicalDevice, surface, swapchain, extent); - } - - private unsafe void CreateSwapchainImages() - { - DestroyCurrentImageViews(); - - Size = new PixelSize((int)_swapchainExtent.Width, (int)_swapchainExtent.Height); - - uint imageCount = 0; - - _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, null); - - _swapchainImages = new Image[imageCount]; - - fixed (Image* pSwapchainImages = _swapchainImages) - { - _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, pSwapchainImages); - } - - _swapchainImageViews = new ImageView[imageCount]; - - var surfaceFormat = SurfaceFormat; - - for (var i = 0; i < imageCount; i++) - { - _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format); - } - } - - private void DestroyCurrentImageViews() - { - for (var i = 0; i < _swapchainImageViews.Length; i++) - { - _instance.Api.DestroyImageView(_device.InternalHandle, _swapchainImageViews[i], Span.Empty); - } - } - - internal void ChangeVSyncMode(bool vsyncEnabled) - { - _vsyncStateChanged = true; - _vsyncEnabled = vsyncEnabled; - } - - private void Recreate() - { - _device.WaitIdle(); - _swapchain = CreateSwapchain(_instance, _device, _physicalDevice, _surface, out _swapchainExtent, _swapchain, _vsyncEnabled); - - CreateSwapchainImages(); - - _surfaceChanged = true; - } - - private unsafe ImageView CreateSwapchainImageView(Image swapchainImage, Format format) - { - var componentMapping = new ComponentMapping( - ComponentSwizzle.Identity, - ComponentSwizzle.Identity, - ComponentSwizzle.Identity, - ComponentSwizzle.Identity); - - var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1); - - var imageCreateInfo = new ImageViewCreateInfo - { - SType = StructureType.ImageViewCreateInfo, - Image = swapchainImage, - ViewType = ImageViewType.ImageViewType2D, - Format = format, - Components = componentMapping, - SubresourceRange = subresourceRange - }; - - _instance.Api.CreateImageView(_device.InternalHandle, imageCreateInfo, null, out var imageView).ThrowOnError(); - return imageView; - } - - public bool EnsureSwapchainAvailable() - { - if (Size != _surface.SurfaceSize || _vsyncStateChanged) - { - _vsyncStateChanged = false; - - Recreate(); - - return false; - } - - return true; - } - - internal VulkanCommandBufferPool.VulkanCommandBuffer StartPresentation() - { - _nextImage = 0; - while (true) - { - var acquireResult = _swapchainExtension.AcquireNextImage( - _device.InternalHandle, - _swapchain, - ulong.MaxValue, - _semaphorePair.ImageAvailableSemaphore, - new Fence(), - ref _nextImage); - - if (acquireResult == Result.ErrorOutOfDateKhr || - acquireResult == Result.SuboptimalKhr) - { - Recreate(); - } - else - { - acquireResult.ThrowOnError(); - break; - } - } - - var commandBuffer = CommandBufferPool.CreateCommandBuffer(); - commandBuffer.BeginRecording(); - - VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle, - _swapchainImages[_nextImage], ImageLayout.Undefined, - AccessFlags.AccessNoneKhr, - ImageLayout.TransferDstOptimal, - AccessFlags.AccessTransferWriteBit, - 1); - - return commandBuffer; - } - - internal void BlitImageToCurrentImage(VulkanSurfaceRenderTarget renderTarget, CommandBuffer commandBuffer) - { - var image = renderTarget.GetImage(); - - VulkanMemoryHelper.TransitionLayout(_device, commandBuffer, - image.InternalHandle.Value, (ImageLayout)image.CurrentLayout, - AccessFlags.AccessNoneKhr, - ImageLayout.TransferSrcOptimal, - AccessFlags.AccessTransferReadBit, - renderTarget.MipLevels); - - var srcBlitRegion = new ImageBlit - { - SrcOffsets = new ImageBlit.SrcOffsetsBuffer - { - Element0 = new Offset3D(0, 0, 0), - Element1 = new Offset3D(renderTarget.Size.Width, renderTarget.Size.Height, 1), - }, - DstOffsets = new ImageBlit.DstOffsetsBuffer - { - Element0 = new Offset3D(0, 0, 0), - Element1 = new Offset3D(Size.Width, Size.Height, 1), - }, - SrcSubresource = new ImageSubresourceLayers - { - AspectMask = ImageAspectFlags.ImageAspectColorBit, - BaseArrayLayer = 0, - LayerCount = 1, - MipLevel = 0 - }, - DstSubresource = new ImageSubresourceLayers - { - AspectMask = ImageAspectFlags.ImageAspectColorBit, - BaseArrayLayer = 0, - LayerCount = 1, - MipLevel = 0 - } - }; - - _device.Api.CmdBlitImage(commandBuffer, image.InternalHandle.Value, - ImageLayout.TransferSrcOptimal, - _swapchainImages[_nextImage], - ImageLayout.TransferDstOptimal, - 1, - srcBlitRegion, - Filter.Linear); - - VulkanMemoryHelper.TransitionLayout(_device, commandBuffer, - image.InternalHandle.Value, ImageLayout.TransferSrcOptimal, - AccessFlags.AccessTransferReadBit, - (ImageLayout)image.CurrentLayout, - AccessFlags.AccessNoneKhr, - renderTarget.MipLevels); - } - - internal unsafe void EndPresentation(VulkanCommandBufferPool.VulkanCommandBuffer commandBuffer) - { - VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle, - _swapchainImages[_nextImage], ImageLayout.TransferDstOptimal, - AccessFlags.AccessNoneKhr, - ImageLayout.PresentSrcKhr, - AccessFlags.AccessNoneKhr, - 1); - - commandBuffer.Submit( - stackalloc[] { _semaphorePair.ImageAvailableSemaphore }, - stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit }, - stackalloc[] { _semaphorePair.RenderFinishedSemaphore }); - - var semaphore = _semaphorePair.RenderFinishedSemaphore; - var swapchain = _swapchain; - var nextImage = _nextImage; - - Result result; - - var presentInfo = new PresentInfoKHR - { - SType = StructureType.PresentInfoKhr, - WaitSemaphoreCount = 1, - PWaitSemaphores = &semaphore, - SwapchainCount = 1, - PSwapchains = &swapchain, - PImageIndices = &nextImage, - PResults = &result - }; - - lock (_device.Lock) - { - _swapchainExtension.QueuePresent(_device.PresentQueue.InternalHandle, presentInfo); - } - - CommandBufferPool.FreeUsedCommandBuffers(); - - Presented?.Invoke(this, null); - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs deleted file mode 100644 index 3fbb8665e..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using Avalonia; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanImage : IDisposable - { - private readonly VulkanDevice _device; - private readonly VulkanPhysicalDevice _physicalDevice; - private readonly VulkanCommandBufferPool _commandBufferPool; - private ImageLayout _currentLayout; - private AccessFlags _currentAccessFlags; - private ImageUsageFlags _imageUsageFlags { get; } - private ImageView? _imageView { get; set; } - private DeviceMemory _imageMemory { get; set; } - - internal Image? InternalHandle { get; private set; } - internal Format Format { get; } - internal ImageAspectFlags AspectFlags { get; private set; } - - public ulong Handle => InternalHandle?.Handle ?? 0; - public ulong ViewHandle => _imageView?.Handle ?? 0; - public uint UsageFlags => (uint)_imageUsageFlags; - public ulong MemoryHandle => _imageMemory.Handle; - public uint MipLevels { get; private set; } - public PixelSize Size { get; } - public ulong MemorySize { get; private set; } - public uint CurrentLayout => (uint)_currentLayout; - - public VulkanImage( - VulkanDevice device, - VulkanPhysicalDevice physicalDevice, - VulkanCommandBufferPool commandBufferPool, - uint format, - PixelSize size, - uint mipLevels = 0) - { - _device = device; - _physicalDevice = physicalDevice; - _commandBufferPool = commandBufferPool; - Format = (Format)format; - Size = size; - MipLevels = mipLevels; - _imageUsageFlags = - ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit | - ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageSampledBit; - - Initialize(); - } - - public unsafe void Initialize() - { - if (!InternalHandle.HasValue) - { - MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2)); - - var imageCreateInfo = new ImageCreateInfo - { - SType = StructureType.ImageCreateInfo, - ImageType = ImageType.ImageType2D, - Format = Format, - Extent = new Extent3D((uint?)Size.Width, (uint?)Size.Height, 1), - MipLevels = MipLevels, - ArrayLayers = 1, - Samples = SampleCountFlags.SampleCount1Bit, - Tiling = Tiling, - Usage = _imageUsageFlags, - SharingMode = SharingMode.Exclusive, - InitialLayout = ImageLayout.Undefined, - Flags = ImageCreateFlags.ImageCreateMutableFormatBit - }; - - _device.Api.CreateImage(_device.InternalHandle, imageCreateInfo, null, out var image).ThrowOnError(); - InternalHandle = image; - - _device.Api.GetImageMemoryRequirements(_device.InternalHandle, InternalHandle.Value, - out var memoryRequirements); - - var memoryAllocateInfo = new MemoryAllocateInfo - { - SType = StructureType.MemoryAllocateInfo, - AllocationSize = memoryRequirements.Size, - MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex( - _physicalDevice, - memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit) - }; - - _device.Api.AllocateMemory(_device.InternalHandle, memoryAllocateInfo, null, - out var imageMemory); - - _imageMemory = imageMemory; - - _device.Api.BindImageMemory(_device.InternalHandle, InternalHandle.Value, _imageMemory, 0); - - MemorySize = memoryRequirements.Size; - - var componentMapping = new ComponentMapping( - ComponentSwizzle.Identity, - ComponentSwizzle.Identity, - ComponentSwizzle.Identity, - ComponentSwizzle.Identity); - - AspectFlags = ImageAspectFlags.ImageAspectColorBit; - - var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1); - - var imageViewCreateInfo = new ImageViewCreateInfo - { - SType = StructureType.ImageViewCreateInfo, - Image = InternalHandle.Value, - ViewType = ImageViewType.ImageViewType2D, - Format = Format, - Components = componentMapping, - SubresourceRange = subresourceRange - }; - - _device.Api - .CreateImageView(_device.InternalHandle, imageViewCreateInfo, null, out var imageView) - .ThrowOnError(); - - _imageView = imageView; - - _currentLayout = ImageLayout.Undefined; - - TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr); - } - } - - public ImageTiling Tiling => ImageTiling.Optimal; - - internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags) - { - var commandBuffer = _commandBufferPool.CreateCommandBuffer(); - commandBuffer.BeginRecording(); - - VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle, InternalHandle.Value, - _currentLayout, - _currentAccessFlags, - destinationLayout, destinationAccessFlags, - MipLevels); - - commandBuffer.EndRecording(); - - commandBuffer.Submit(); - - _currentLayout = destinationLayout; - _currentAccessFlags = destinationAccessFlags; - } - - public void Dispose() - { - if (InternalHandle != null) - { - _device.Api.DestroyImageView(_device.InternalHandle, _imageView.Value, Span.Empty); - _device.Api.DestroyImage(_device.InternalHandle, InternalHandle.Value, Span.Empty); - _device.Api.FreeMemory(_device.InternalHandle, _imageMemory, Span.Empty); - - _imageView = default; - InternalHandle = null; - _imageMemory = default; - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs deleted file mode 100644 index b50e9c07d..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using Silk.NET.Core; -using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.EXT; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - public class VulkanInstance : IDisposable - { - private const string EngineName = "Avalonia Vulkan"; - - private VulkanInstance(Instance apiHandle, Vk api) - { - InternalHandle = apiHandle; - Api = api; - } - - public IntPtr Handle => InternalHandle.Handle; - - internal Instance InternalHandle { get; } - public Vk Api { get; } - - internal static IEnumerable RequiredInstanceExtensions - { - get - { - yield return "VK_KHR_surface"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - yield return "VK_KHR_xlib_surface"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - yield return "VK_KHR_win32_surface"; - } - } - } - - public void Dispose() - { - Api?.DestroyInstance(InternalHandle, Span.Empty); - Api?.Dispose(); - } - - internal static unsafe VulkanInstance Create(VulkanOptions options) - { - var api = Vk.GetApi(); - var applicationName = Marshal.StringToHGlobalAnsi(options.ApplicationName); - var engineName = Marshal.StringToHGlobalAnsi(EngineName); - var enabledExtensions = new List(options.InstanceExtensions); - - enabledExtensions.AddRange(RequiredInstanceExtensions); - - var applicationInfo = new ApplicationInfo - { - PApplicationName = (byte*)applicationName, - ApiVersion = Vk.Version12.Value, - PEngineName = (byte*)engineName, - EngineVersion = new Version32(1, 0, 0), - ApplicationVersion = new Version32(1, 0, 0) - }; - - var enabledLayers = new HashSet(); - - if (options.UseDebug) - { - enabledExtensions.Add(ExtDebugUtils.ExtensionName); - enabledExtensions.Add(ExtDebugReport.ExtensionName); - if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation")) - enabledLayers.Add("VK_LAYER_KHRONOS_validation"); - } - - foreach (var layer in options.EnabledLayers) - enabledLayers.Add(layer); - - var ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Count]; - var ppEnabledLayers = stackalloc IntPtr[enabledLayers.Count]; - - for (var i = 0; i < enabledExtensions.Count; i++) - ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]); - - var layers = enabledLayers.ToList(); - - for (var i = 0; i < enabledLayers.Count; i++) - ppEnabledLayers[i] = Marshal.StringToHGlobalAnsi(layers[i]); - - var instanceCreateInfo = new InstanceCreateInfo - { - SType = StructureType.InstanceCreateInfo, - PApplicationInfo = &applicationInfo, - PpEnabledExtensionNames = (byte**)ppEnabledExtensions, - PpEnabledLayerNames = (byte**)ppEnabledLayers, - EnabledExtensionCount = (uint)enabledExtensions.Count, - EnabledLayerCount = (uint)enabledLayers.Count - }; - - api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); - - Marshal.FreeHGlobal(applicationName); - Marshal.FreeHGlobal(engineName); - - for (var i = 0; i < enabledExtensions.Count; i++) Marshal.FreeHGlobal(ppEnabledExtensions[i]); - - for (var i = 0; i < enabledLayers.Count; i++) Marshal.FreeHGlobal(ppEnabledLayers[i]); - - return new VulkanInstance(instance, api); - } - - private static unsafe bool IsLayerAvailable(Vk api, string layerName) - { - uint layerPropertiesCount; - - api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError(); - - var layerProperties = new LayerProperties[layerPropertiesCount]; - - fixed (LayerProperties* pLayerProperties = layerProperties) - { - api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError(); - - for (var i = 0; i < layerPropertiesCount; i++) - { - var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName); - - if (currentLayerName == layerName) return true; - } - } - - return false; - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs deleted file mode 100644 index a70525920..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal static class VulkanMemoryHelper - { - internal static int FindSuitableMemoryTypeIndex(VulkanPhysicalDevice physicalDevice, uint memoryTypeBits, - MemoryPropertyFlags flags) - { - physicalDevice.Api.GetPhysicalDeviceMemoryProperties(physicalDevice.InternalHandle, out var properties); - - for (var i = 0; i < properties.MemoryTypeCount; i++) - { - var type = properties.MemoryTypes[i]; - - if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i; - } - - return -1; - } - - internal static unsafe void TransitionLayout(VulkanDevice device, - CommandBuffer commandBuffer, - Image image, - ImageLayout sourceLayout, - AccessFlags sourceAccessMask, - ImageLayout destinationLayout, - AccessFlags destinationAccessMask, - uint mipLevels) - { - var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, mipLevels, 0, 1); - - var barrier = new ImageMemoryBarrier - { - SType = StructureType.ImageMemoryBarrier, - SrcAccessMask = sourceAccessMask, - DstAccessMask = destinationAccessMask, - OldLayout = sourceLayout, - NewLayout = destinationLayout, - SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, - DstQueueFamilyIndex = Vk.QueueFamilyIgnored, - Image = image, - SubresourceRange = subresourceRange - }; - - device.Api.CmdPipelineBarrier( - commandBuffer, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - 0, - 0, - null, - 0, - null, - 1, - barrier); - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs deleted file mode 100644 index 0027753c8..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - public class VulkanOptions - { - /// - /// Sets the application name of the Vulkan instance - /// - public string ApplicationName { get; set; } - - /// - /// Specifies additional extensions to enable if available on the instance - /// - public IEnumerable InstanceExtensions { get; set; } = Enumerable.Empty(); - - /// - /// Specifies layers to enable if available on the instance - /// - public IEnumerable EnabledLayers { get; set; } = Enumerable.Empty(); - - /// - /// Enables the debug layer - /// - public bool UseDebug { get; set; } - - /// - /// Selects the first suitable discrete GPU available - /// - public bool PreferDiscreteGpu { get; set; } - - /// - /// Sets the device to use if available and suitable. - /// - public string PreferredDevice { get; set; } - - /// - /// Max number of device queues to request - /// - public uint MaxQueueCount { get; set; } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs deleted file mode 100644 index 11444d30c..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs +++ /dev/null @@ -1,219 +0,0 @@ -using Ryujinx.Graphics.Vulkan; -using Silk.NET.Core; -using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.KHR; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - public unsafe class VulkanPhysicalDevice - { - private VulkanPhysicalDevice(PhysicalDevice apiHandle, Vk api, uint queueCount, uint queueFamilyIndex) - { - InternalHandle = apiHandle; - Api = api; - QueueCount = queueCount; - QueueFamilyIndex = queueFamilyIndex; - - api.GetPhysicalDeviceProperties(apiHandle, out var properties); - - DeviceName = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); - DeviceId = VulkanInitialization.StringFromIdPair(properties.VendorID, properties.DeviceID); - - var version = (Version32)properties.ApiVersion; - ApiVersion = new Version((int)version.Major, (int)version.Minor, 0, (int)version.Patch); - } - - internal PhysicalDevice InternalHandle { get; } - internal Vk Api { get; } - public uint QueueCount { get; } - public uint QueueFamilyIndex { get; } - public IntPtr Handle => InternalHandle.Handle; - - public string DeviceName { get; } - public string DeviceId { get; } - public Version ApiVersion { get; } - public static Dictionary PhysicalDevices { get; private set; } - public static IEnumerable> SuitableDevices { get; private set; } - - internal static void SelectAvailableDevices(VulkanInstance instance, - VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice) - { - uint physicalDeviceCount; - - instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, null).ThrowOnError(); - - var physicalDevices = new PhysicalDevice[physicalDeviceCount]; - - fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) - { - instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, pPhysicalDevices) - .ThrowOnError(); - } - - PhysicalDevices = new Dictionary(); - - foreach (var physicalDevice in physicalDevices) - { - instance.Api.GetPhysicalDeviceProperties(physicalDevice, out var properties); - PhysicalDevices.Add(physicalDevice, properties); - } - - SuitableDevices = PhysicalDevices.Where(x => IsSuitableDevice( - instance.Api, - x.Key, - x.Value, - surface.ApiHandle, - out _, - out _)); - } - - internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(VulkanInstance instance, - VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice) - { - SelectAvailableDevices(instance, surface, preferDiscreteGpu, preferredDevice); - - uint queueFamilyIndex = 0; - uint queueCount = 0; - - if (!string.IsNullOrWhiteSpace(preferredDevice)) - { - var physicalDevice = SuitableDevices.FirstOrDefault(x => VulkanInitialization.StringFromIdPair(x.Value.VendorID, x.Value.DeviceID) == preferredDevice); - - queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key, - surface.ApiHandle, out queueCount); - if (queueFamilyIndex != int.MaxValue) - { - return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex); - } - } - - if (preferDiscreteGpu) - { - var discreteGpus = SuitableDevices.Where(p => p.Value.DeviceType == PhysicalDeviceType.DiscreteGpu); - - foreach (var gpu in discreteGpus) - { - queueFamilyIndex = FindSuitableQueueFamily(instance.Api, gpu.Key, - surface.ApiHandle, out queueCount); - if (queueFamilyIndex != int.MaxValue) - { - return new VulkanPhysicalDevice(gpu.Key, instance.Api, queueCount, queueFamilyIndex); - } - } - } - - foreach (var physicalDevice in SuitableDevices) - { - queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key, - surface.ApiHandle, out queueCount); - if (queueFamilyIndex != int.MaxValue) - { - return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex); - } - } - - throw new Exception("No suitable physical device found"); - } - - private static unsafe bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, PhysicalDeviceProperties properties, SurfaceKHR surface, - out uint queueCount, out uint familyIndex) - { - queueCount = 0; - familyIndex = 0; - - if (properties.DeviceType == PhysicalDeviceType.Cpu) return false; - - var extensionMatches = 0; - uint propertiesCount; - - api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); - - var extensionProperties = new ExtensionProperties[propertiesCount]; - - fixed (ExtensionProperties* pExtensionProperties = extensionProperties) - { - api.EnumerateDeviceExtensionProperties( - physicalDevice, - (byte*)null, - &propertiesCount, - pExtensionProperties).ThrowOnError(); - - for (var i = 0; i < propertiesCount; i++) - { - var extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName); - - if (VulkanInitialization.RequiredExtensions.Contains(extensionName)) - { - extensionMatches++; - } - } - } - - if (extensionMatches == VulkanInitialization.RequiredExtensions.Length) - { - familyIndex = FindSuitableQueueFamily(api, physicalDevice, surface, out queueCount); - - return familyIndex != uint.MaxValue; - } - - return false; - } - - internal unsafe string[] GetSupportedExtensions() - { - uint propertiesCount; - - Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, null).ThrowOnError(); - - var extensionProperties = new ExtensionProperties[propertiesCount]; - - fixed (ExtensionProperties* pExtensionProperties = extensionProperties) - { - Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, pExtensionProperties) - .ThrowOnError(); - } - - return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray(); - } - - private static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, - out uint queueCount) - { - const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit; - - var khrSurface = new KhrSurface(api.Context); - - uint propertiesCount; - - api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null); - - var properties = new QueueFamilyProperties[propertiesCount]; - - fixed (QueueFamilyProperties* pProperties = properties) - { - api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties); - } - - for (uint index = 0; index < propertiesCount; index++) - { - var queueFlags = properties[index].QueueFlags; - - khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported) - .ThrowOnError(); - - if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported) - { - queueCount = properties[index].QueueCount; - return index; - } - } - - queueCount = 0; - return uint.MaxValue; - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs deleted file mode 100644 index ff8d93286..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Avalonia; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Ryujinx.Graphics.Vulkan; -using Silk.NET.Vulkan; -using System; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanPlatformInterface : IDisposable - { - private static VulkanOptions _options; - - private VulkanPlatformInterface(VulkanInstance instance) - { - Instance = instance; - Api = instance.Api; - } - - public VulkanPhysicalDevice PhysicalDevice { get; private set; } - public VulkanInstance Instance { get; } - public Vk Api { get; private set; } - public VulkanSurfaceRenderTarget MainSurface { get; set; } - - public void Dispose() - { - Instance?.Dispose(); - Api?.Dispose(); - } - - private static VulkanPlatformInterface TryCreate() - { - _options = AvaloniaLocator.Current.GetService() ?? new VulkanOptions(); - - var instance = VulkanInstance.Create(_options); - - return new VulkanPlatformInterface(instance); - } - - public static bool TryInitialize() - { - var feature = TryCreate(); - if (feature != null) - { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); - return true; - } - - return false; - } - - public VulkanSurfaceRenderTarget CreateRenderTarget(IVulkanPlatformSurface platformSurface) - { - var surface = VulkanSurface.CreateSurface(Instance, platformSurface); - - if (PhysicalDevice == null) - { - PhysicalDevice = VulkanPhysicalDevice.FindSuitablePhysicalDevice(Instance, surface, _options.PreferDiscreteGpu, _options.PreferredDevice); - } - - var renderTarget = new VulkanSurfaceRenderTarget(this, surface); - - if (MainSurface == null && surface != null) - { - MainSurface = renderTarget; - } - - return renderTarget; - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs deleted file mode 100644 index a903e21a6..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanQueue - { - public VulkanQueue(VulkanDevice device, Queue apiHandle) - { - Device = device; - InternalHandle = apiHandle; - } - - public VulkanDevice Device { get; } - public IntPtr Handle => InternalHandle.Handle; - internal Queue InternalHandle { get; } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs deleted file mode 100644 index 3b5fd9cc6..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanSemaphorePair : IDisposable - { - private readonly VulkanDevice _device; - - public unsafe VulkanSemaphorePair(VulkanDevice device) - { - _device = device; - - var semaphoreCreateInfo = new SemaphoreCreateInfo { SType = StructureType.SemaphoreCreateInfo }; - - _device.Api.CreateSemaphore(_device.InternalHandle, semaphoreCreateInfo, null, out var semaphore).ThrowOnError(); - ImageAvailableSemaphore = semaphore; - - _device.Api.CreateSemaphore(_device.InternalHandle, semaphoreCreateInfo, null, out semaphore).ThrowOnError(); - RenderFinishedSemaphore = semaphore; - } - - internal Semaphore ImageAvailableSemaphore { get; } - internal Semaphore RenderFinishedSemaphore { get; } - - public unsafe void Dispose() - { - _device.Api.DestroySemaphore(_device.InternalHandle, ImageAvailableSemaphore, null); - _device.Api.DestroySemaphore(_device.InternalHandle, RenderFinishedSemaphore, null); - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs deleted file mode 100644 index 2452cdcd7..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using Avalonia; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Silk.NET.Vulkan; -using Silk.NET.Vulkan.Extensions.KHR; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - public class VulkanSurface : IDisposable - { - private readonly VulkanInstance _instance; - private readonly IVulkanPlatformSurface _vulkanPlatformSurface; - - private VulkanSurface(IVulkanPlatformSurface vulkanPlatformSurface, VulkanInstance instance) - { - _vulkanPlatformSurface = vulkanPlatformSurface; - _instance = instance; - ApiHandle = vulkanPlatformSurface.CreateSurface(instance); - } - - internal SurfaceKHR ApiHandle { get; } - - internal static KhrSurface SurfaceExtension { get; private set; } - - internal PixelSize SurfaceSize => _vulkanPlatformSurface.SurfaceSize; - - public void Dispose() - { - SurfaceExtension.DestroySurface(_instance.InternalHandle, ApiHandle, Span.Empty); - _vulkanPlatformSurface.Dispose(); - } - - internal static VulkanSurface CreateSurface(VulkanInstance instance, IVulkanPlatformSurface vulkanPlatformSurface) - { - if (SurfaceExtension == null) - { - instance.Api.TryGetInstanceExtension(instance.InternalHandle, out KhrSurface extension); - - SurfaceExtension = extension; - } - - return new VulkanSurface(vulkanPlatformSurface, instance); - } - - internal bool CanSurfacePresent(VulkanPhysicalDevice physicalDevice) - { - SurfaceExtension.GetPhysicalDeviceSurfaceSupport(physicalDevice.InternalHandle, physicalDevice.QueueFamilyIndex, ApiHandle, out var isSupported); - - return isSupported; - } - - internal SurfaceFormatKHR GetSurfaceFormat(VulkanPhysicalDevice physicalDevice) - { - Span surfaceFormatsCount = stackalloc uint[1]; - SurfaceExtension.GetPhysicalDeviceSurfaceFormats(physicalDevice.InternalHandle, ApiHandle, surfaceFormatsCount, Span.Empty); - Span surfaceFormats = stackalloc SurfaceFormatKHR[(int)surfaceFormatsCount[0]]; - SurfaceExtension.GetPhysicalDeviceSurfaceFormats(physicalDevice.InternalHandle, ApiHandle, surfaceFormatsCount, surfaceFormats); - - if (surfaceFormats.Length == 1 && surfaceFormats[0].Format == Format.Undefined) - { - return new SurfaceFormatKHR(Format.B8G8R8A8Unorm, ColorSpaceKHR.ColorspaceSrgbNonlinearKhr); - } - - foreach (var format in surfaceFormats) - { - if (format.Format == Format.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.ColorspaceSrgbNonlinearKhr) - { - return format; - } - } - - return surfaceFormats[0]; - } - } -} diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs deleted file mode 100644 index 71f5f18ac..000000000 --- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Avalonia; -using Ryujinx.Ava.Ui.Vulkan.Surfaces; -using Silk.NET.Vulkan; - -namespace Ryujinx.Ava.Ui.Vulkan -{ - internal class VulkanSurfaceRenderingSession : IDisposable - { - private readonly VulkanDevice _device; - private readonly VulkanSurfaceRenderTarget _renderTarget; - - public VulkanSurfaceRenderingSession(VulkanDisplay display, VulkanDevice device, - VulkanSurfaceRenderTarget renderTarget, float scaling) - { - Display = display; - _device = device; - _renderTarget = renderTarget; - Scaling = scaling; - Begin(); - } - - public VulkanDisplay Display { get; } - - public PixelSize Size => _renderTarget.Size; - public Vk Api => _device.Api; - - public float Scaling { get; } - - private void Begin() - { - if (!Display.EnsureSwapchainAvailable()) - { - _renderTarget.RecreateImage(); - } - } - - public void Dispose() - { - _renderTarget.EndDraw(); - } - } -} diff --git a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs index 15ecaa77b..e774a09a0 100644 --- a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs +++ b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs @@ -1,4 +1,8 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Controls.Primitives; +using Avalonia.Media; using Avalonia.Threading; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; @@ -27,9 +31,69 @@ namespace Ryujinx.Ava.Ui.Controls { UserResult result = UserResult.None; - ContentDialog contentDialog = new ContentDialog(); + bool useOverlay = false; + Window mainWindow = null; - await ShowDialog(); + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) + { + foreach (var item in al.Windows) + { + if (item.IsActive && item is MainWindow window && window.ViewModel.IsGameRunning) + { + mainWindow = window; + useOverlay = true; + break; + } + } + } + + ContentDialog contentDialog = null; + ContentDialogOverlayWindow overlay = null; + + if (useOverlay) + { + overlay = new ContentDialogOverlayWindow() + { + Height = mainWindow.Bounds.Height, + Width = mainWindow.Bounds.Width, + Position = mainWindow.PointToScreen(new Point()) + }; + + mainWindow.PositionChanged += OverlayOnPositionChanged; + + void OverlayOnPositionChanged(object sender, PixelPointEventArgs e) + { + overlay.Position = mainWindow.PointToScreen(new Point()); + } + + contentDialog = overlay.ContentDialog; + + bool opened = false; + + overlay.Opened += OverlayOnActivated; + + async void OverlayOnActivated(object sender, EventArgs e) + { + if (opened) + { + return; + } + + opened = true; + + overlay.Position = mainWindow.PointToScreen(new Point()); + + await ShowDialog(); + } + + await overlay.ShowDialog(mainWindow); + } + else + { + contentDialog = new ContentDialog(); + + await ShowDialog(); + } async Task ShowDialog() { @@ -53,6 +117,14 @@ namespace Ryujinx.Ava.Ui.Controls }); await contentDialog.ShowAsync(ContentDialogPlacement.Popup); + + overlay?.Close(); + } + + if (useOverlay) + { + overlay.Content = null; + overlay.Close(); } return result; @@ -323,4 +395,4 @@ namespace Ryujinx.Ava.Ui.Controls return string.Empty; } } -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs new file mode 100644 index 000000000..d9fae93a5 --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs @@ -0,0 +1,204 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Platform; +using SPB.Graphics; +using SPB.Platform; +using SPB.Platform.GLX; +using SPB.Platform.X11; +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using static Ryujinx.Ava.Ui.Controls.Win32NativeInterop; + +namespace Ryujinx.Ava.Ui.Controls +{ + public unsafe class EmbeddedWindow : NativeControlHost + { + private WindowProc _wndProcDelegate; + private string _className; + + protected GLXWindow X11Window { get; private set; } + protected IntPtr WindowHandle { get; set; } + protected IntPtr X11Display { get; set; } + + public event EventHandler WindowCreated; + public event EventHandler SizeChanged; + + protected virtual void OnWindowDestroyed() { } + protected virtual void OnWindowDestroying() + { + WindowHandle = IntPtr.Zero; + X11Display = IntPtr.Zero; + } + + public EmbeddedWindow() + { + var stateObserverable = this.GetObservable(Control.BoundsProperty); + + stateObserverable.Subscribe(StateChanged); + + this.Initialized += NativeEmbeddedWindow_Initialized; + } + + public virtual void OnWindowCreated() { } + + private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e) + { + OnWindowCreated(); + + Task.Run(() => + { + WindowCreated?.Invoke(this, WindowHandle); + }); + } + + private void StateChanged(Rect rect) + { + SizeChanged?.Invoke(this, rect.Size); + } + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) + { + if (OperatingSystem.IsLinux()) + { + return CreateLinux(parent); + } + else if (OperatingSystem.IsWindows()) + { + return CreateWin32(parent); + } + return base.CreateNativeControlCore(parent); + } + + protected override void DestroyNativeControlCore(IPlatformHandle control) + { + OnWindowDestroying(); + + if (OperatingSystem.IsLinux()) + { + DestroyLinux(); + } + else if (OperatingSystem.IsWindows()) + { + DestroyWin32(control); + } + else + { + base.DestroyNativeControlCore(control); + } + + OnWindowDestroyed(); + } + + [SupportedOSPlatform("linux")] + IPlatformHandle CreateLinux(IPlatformHandle parent) + { + X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; + + WindowHandle = X11Window.WindowHandle.RawHandle; + + X11Display = X11Window.DisplayHandle.RawHandle; + + return new PlatformHandle(WindowHandle, "X11"); + } + + [SupportedOSPlatform("windows")] + unsafe IPlatformHandle CreateWin32(IPlatformHandle parent) + { + _className = "NativeWindow-" + Guid.NewGuid(); + _wndProcDelegate = WndProc; + var wndClassEx = new WNDCLASSEX + { + cbSize = Marshal.SizeOf(), + hInstance = GetModuleHandle(null), + lpfnWndProc = _wndProcDelegate, + style = ClassStyles.CS_OWNDC, + lpszClassName = _className, + hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW) + }; + + var atom = RegisterClassEx(ref wndClassEx); + + var handle = CreateWindowEx( + 0, + _className, + "NativeWindow", + WindowStyles.WS_CHILD, + 0, + 0, + 640, + 480, + parent.Handle, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero); + + WindowHandle = handle; + + return new PlatformHandle(WindowHandle, "HWND"); + } + + [SupportedOSPlatform("windows")] + internal IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) + { + var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF); + var root = VisualRoot as Window; + bool isLeft = false; + switch (msg) + { + case WindowsMessages.LBUTTONDOWN: + case WindowsMessages.RBUTTONDOWN: + isLeft = msg == WindowsMessages.LBUTTONDOWN; + this.RaiseEvent(new PointerPressedEventArgs( + this, + new Avalonia.Input.Pointer(0, PointerType.Mouse, true), + root, + this.TranslatePoint(point, root).Value, + (ulong)Environment.TickCount64, + new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed), + KeyModifiers.None)); + break; + case WindowsMessages.LBUTTONUP: + case WindowsMessages.RBUTTONUP: + isLeft = msg == WindowsMessages.LBUTTONUP; + this.RaiseEvent(new PointerReleasedEventArgs( + this, + new Avalonia.Input.Pointer(0, PointerType.Mouse, true), + root, + this.TranslatePoint(point, root).Value, + (ulong)Environment.TickCount64, + new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased), + KeyModifiers.None, + isLeft ? MouseButton.Left : MouseButton.Right)); + break; + case WindowsMessages.MOUSEMOVE: + this.RaiseEvent(new PointerEventArgs( + PointerMovedEvent, + this, + new Avalonia.Input.Pointer(0, PointerType.Mouse, true), + root, + this.TranslatePoint(point, root).Value, + (ulong)Environment.TickCount64, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), + KeyModifiers.None)); + break; + } + return DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam); + } + + void DestroyLinux() + { + X11Window?.Dispose(); + } + + [SupportedOSPlatform("windows")] + void DestroyWin32(IPlatformHandle handle) + { + DestroyWindow(handle.Handle); + UnregisterClass(_className, GetModuleHandle(null)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs new file mode 100644 index 000000000..ce579fdfd --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs @@ -0,0 +1,85 @@ +using Avalonia; +using Avalonia.OpenGL; +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Configuration; +using SPB.Graphics; +using SPB.Graphics.OpenGL; +using SPB.Platform; +using SPB.Platform.GLX; +using SPB.Platform.WGL; +using SPB.Windowing; +using System; + +namespace Ryujinx.Ava.Ui.Controls +{ + public class OpenGLEmbeddedWindow : EmbeddedWindow + { + private readonly int _major; + private readonly int _minor; + private readonly GraphicsDebugLevel _graphicsDebugLevel; + private SwappableNativeWindowBase _window; + public OpenGLContextBase Context { get; set; } + + public OpenGLEmbeddedWindow(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) + { + _major = major; + _minor = minor; + _graphicsDebugLevel = graphicsDebugLevel; + } + + protected override void OnWindowDestroying() + { + Context.Dispose(); + base.OnWindowDestroying(); + } + + public override void OnWindowCreated() + { + base.OnWindowCreated(); + + if (OperatingSystem.IsWindows()) + { + _window = new WGLWindow(new NativeHandle(WindowHandle)); + } + else if (OperatingSystem.IsLinux()) + { + _window = X11Window; + } + else + { + throw new PlatformNotSupportedException(); + } + + var flags = OpenGLContextFlags.Compat; + if (_graphicsDebugLevel != GraphicsDebugLevel.None) + { + flags |= OpenGLContextFlags.Debug; + } + + Context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, _major, _minor, flags); + + Context.Initialize(_window); + Context.MakeCurrent(_window); + + var bindingsContext = new OpenToolkitBindingsContext(Context.GetProcAddress); + + GL.LoadBindings(bindingsContext); + Context.MakeCurrent(null); + } + + public void MakeCurrent() + { + Context.MakeCurrent(_window); + } + + public void MakeCurrent(NativeWindowBase window) + { + Context.MakeCurrent(window); + } + + public void SwapBuffers() + { + _window.SwapBuffers(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs deleted file mode 100644 index e58bdaa0a..000000000 --- a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs +++ /dev/null @@ -1,192 +0,0 @@ -using Avalonia; -using Avalonia.OpenGL; -using Avalonia.Platform; -using Avalonia.Rendering.SceneGraph; -using Avalonia.Skia; -using Avalonia.Threading; -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Configuration; -using SkiaSharp; -using SPB.Graphics; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Windowing; -using System; - -namespace Ryujinx.Ava.Ui.Controls -{ - internal class OpenGLRendererControl : RendererControl - { - public int Major { get; } - public int Minor { get; } - public OpenGLContextBase GameContext { get; set; } - - public static OpenGLContextBase PrimaryContext => - AvaloniaLocator.Current.GetService() - .PrimaryContext.AsOpenGLContextBase(); - - private SwappableNativeWindowBase _gameBackgroundWindow; - - private IntPtr _fence; - - public OpenGLRendererControl(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel) - { - Major = major; - Minor = minor; - } - - public override void DestroyBackgroundContext() - { - Image = null; - - if (_fence != IntPtr.Zero) - { - DrawOperation.Dispose(); - GL.DeleteSync(_fence); - } - - GlDrawOperation.DeleteFramebuffer(); - - GameContext?.Dispose(); - - _gameBackgroundWindow?.Dispose(); - } - - internal override void Present(object image) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Image = (int)image; - - InvalidateVisual(); - }).Wait(); - - if (_fence != IntPtr.Zero) - { - GL.DeleteSync(_fence); - } - - _fence = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); - - InvalidateVisual(); - - _gameBackgroundWindow.SwapBuffers(); - } - - internal override void MakeCurrent() - { - GameContext.MakeCurrent(_gameBackgroundWindow); - } - - internal override void MakeCurrent(SwappableNativeWindowBase window) - { - GameContext.MakeCurrent(window); - } - - protected override void CreateWindow() - { - var flags = OpenGLContextFlags.Compat; - if (DebugLevel != GraphicsDebugLevel.None) - { - flags |= OpenGLContextFlags.Debug; - } - _gameBackgroundWindow = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); - _gameBackgroundWindow.Hide(); - - GameContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, Major, Minor, flags, shareContext: PrimaryContext); - GameContext.Initialize(_gameBackgroundWindow); - } - - protected override ICustomDrawOperation CreateDrawOperation() - { - return new GlDrawOperation(this); - } - - private class GlDrawOperation : ICustomDrawOperation - { - private static int _framebuffer; - - public Rect Bounds { get; } - - private readonly OpenGLRendererControl _control; - - public GlDrawOperation(OpenGLRendererControl control) - { - _control = control; - Bounds = _control.Bounds; - } - - public void Dispose() { } - - public static void DeleteFramebuffer() - { - if (_framebuffer == 0) - { - GL.DeleteFramebuffer(_framebuffer); - } - - _framebuffer = 0; - } - - public bool Equals(ICustomDrawOperation other) - { - return other is GlDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds; - } - - public bool HitTest(Point p) - { - return Bounds.Contains(p); - } - - private void CreateRenderTarget() - { - _framebuffer = GL.GenFramebuffer(); - } - - public void Render(IDrawingContextImpl context) - { - if (_control.Image == null) - { - return; - } - - if (_framebuffer == 0) - { - CreateRenderTarget(); - } - - int currentFramebuffer = GL.GetInteger(GetPName.FramebufferBinding); - - var image = _control.Image; - var fence = _control._fence; - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, (int)image, 0); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, currentFramebuffer); - - if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) - { - return; - } - - var imageInfo = new SKImageInfo((int)_control.RenderSize.Width, (int)_control.RenderSize.Height, SKColorType.Rgba8888); - var glInfo = new GRGlFramebufferInfo((uint)_framebuffer, SKColorType.Rgba8888.ToGlSizedFormat()); - - GL.WaitSync(fence, WaitSyncFlags.None, ulong.MaxValue); - - using var backendTexture = new GRBackendRenderTarget(imageInfo.Width, imageInfo.Height, 1, 0, glInfo); - using var surface = SKSurface.Create(skiaDrawingContextImpl.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); - - if (surface == null) - { - return; - } - - var rect = new Rect(new Point(), _control.RenderSize); - - using var snapshot = surface.Snapshot(); - skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint()); - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Controls/RendererControl.cs b/Ryujinx.Ava/Ui/Controls/RendererControl.cs deleted file mode 100644 index 392f67e35..000000000 --- a/Ryujinx.Ava/Ui/Controls/RendererControl.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Data; -using Avalonia.Media; -using Avalonia.Rendering.SceneGraph; -using Ryujinx.Common.Configuration; -using SPB.Windowing; -using System; - -namespace Ryujinx.Ava.Ui.Controls -{ - internal abstract class RendererControl : Control - { - protected object Image { get; set; } - - public event EventHandler RendererInitialized; - public event EventHandler SizeChanged; - - protected Size RenderSize { get; private set; } - public bool IsStarted { get; private set; } - - public GraphicsDebugLevel DebugLevel { get; } - - private bool _isInitialized; - - protected ICustomDrawOperation DrawOperation { get; private set; } - - public RendererControl(GraphicsDebugLevel graphicsDebugLevel) - { - DebugLevel = graphicsDebugLevel; - IObservable resizeObservable = this.GetObservable(BoundsProperty); - - resizeObservable.Subscribe(Resized); - - Focusable = true; - } - - protected void Resized(Rect rect) - { - SizeChanged?.Invoke(this, rect.Size); - - if (!rect.IsEmpty) - { - RenderSize = rect.Size * VisualRoot.RenderScaling; - DrawOperation = CreateDrawOperation(); - } - } - - protected abstract ICustomDrawOperation CreateDrawOperation(); - protected abstract void CreateWindow(); - - public override void Render(DrawingContext context) - { - if (!_isInitialized) - { - CreateWindow(); - - OnRendererInitialized(); - _isInitialized = true; - } - - if (!IsStarted || Image == null) - { - return; - } - - if (DrawOperation != null) - { - context.Custom(DrawOperation); - } - - base.Render(context); - } - - protected void OnRendererInitialized() - { - RendererInitialized?.Invoke(this, EventArgs.Empty); - } - - internal abstract void Present(object image); - - internal void Start() - { - IsStarted = true; - } - - internal void Stop() - { - IsStarted = false; - } - - public abstract void DestroyBackgroundContext(); - internal abstract void MakeCurrent(); - internal abstract void MakeCurrent(SwappableNativeWindowBase window); - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml new file mode 100644 index 000000000..be72fd61e --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml @@ -0,0 +1,14 @@ + + + diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs new file mode 100644 index 000000000..0d1984fd5 --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs @@ -0,0 +1,126 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Ryujinx.Common.Configuration; +using Silk.NET.Vulkan; +using SPB.Graphics.OpenGL; +using SPB.Windowing; +using System; + +namespace Ryujinx.Ava.Ui.Controls +{ + public partial class RendererHost : UserControl, IDisposable + { + private readonly GraphicsDebugLevel _graphicsDebugLevel; + private EmbeddedWindow _currentWindow; + + public bool IsVulkan { get; private set; } + + public RendererHost(GraphicsDebugLevel graphicsDebugLevel) + { + _graphicsDebugLevel = graphicsDebugLevel; + InitializeComponent(); + } + + public RendererHost() + { + InitializeComponent(); + } + + public void CreateOpenGL() + { + Dispose(); + + _currentWindow = new OpenGLEmbeddedWindow(3, 3, _graphicsDebugLevel); + Initialize(); + + IsVulkan = false; + } + + private void Initialize() + { + _currentWindow.WindowCreated += CurrentWindow_WindowCreated; + _currentWindow.SizeChanged += CurrentWindow_SizeChanged; + View.Content = _currentWindow; + } + + public void CreateVulkan() + { + Dispose(); + + _currentWindow = new VulkanEmbeddedWindow(); + Initialize(); + + IsVulkan = true; + } + + public OpenGLContextBase GetContext() + { + if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) + { + return openGlEmbeddedWindow.Context; + } + + return null; + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + Dispose(); + } + + private void CurrentWindow_SizeChanged(object sender, Size e) + { + SizeChanged?.Invoke(sender, e); + } + + private void CurrentWindow_WindowCreated(object sender, IntPtr e) + { + RendererInitialized?.Invoke(this, EventArgs.Empty); + } + + public void MakeCurrent() + { + if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) + { + openGlEmbeddedWindow.MakeCurrent(); + } + } + + public void MakeCurrent(SwappableNativeWindowBase window) + { + if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) + { + openGlEmbeddedWindow.MakeCurrent(window); + } + } + + public void SwapBuffers() + { + if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) + { + openGlEmbeddedWindow.SwapBuffers(); + } + } + + public event EventHandler RendererInitialized; + public event Action SizeChanged; + public void Dispose() + { + if (_currentWindow != null) + { + _currentWindow.WindowCreated -= CurrentWindow_WindowCreated; + _currentWindow.SizeChanged -= CurrentWindow_SizeChanged; + } + } + + public SurfaceKHR CreateVulkanSurface(Instance instance, Vk api) + { + return (_currentWindow is VulkanEmbeddedWindow vulkanEmbeddedWindow) + ? vulkanEmbeddedWindow.CreateSurface(instance) + : default; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs new file mode 100644 index 000000000..d2c980ddf --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs @@ -0,0 +1,33 @@ +using Ryujinx.Ava.Ui.Controls; +using Silk.NET.Vulkan; +using SPB.Graphics.Vulkan; +using SPB.Platform.Win32; +using SPB.Platform.X11; +using SPB.Windowing; +using System; + +namespace Ryujinx.Ava.Ui +{ + public class VulkanEmbeddedWindow : EmbeddedWindow + { + private NativeWindowBase _window; + + public SurfaceKHR CreateSurface(Instance instance) + { + if (OperatingSystem.IsWindows()) + { + _window = new SimpleWin32Window(new NativeHandle(WindowHandle)); + } + else if (OperatingSystem.IsLinux()) + { + _window = X11Window; + } + else + { + throw new PlatformNotSupportedException(); + } + + return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, _window)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs deleted file mode 100644 index 7b7dfaa10..000000000 --- a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs +++ /dev/null @@ -1,220 +0,0 @@ -using Avalonia; -using Avalonia.Media; -using Avalonia.Platform; -using Avalonia.Rendering.SceneGraph; -using Avalonia.Skia; -using Avalonia.Threading; -using Ryujinx.Ava.Ui.Backend.Vulkan; -using Ryujinx.Ava.Ui.Vulkan; -using Ryujinx.Common.Configuration; -using Ryujinx.Graphics.Vulkan; -using Silk.NET.Vulkan; -using SkiaSharp; -using SPB.Windowing; -using System; -using System.Collections.Concurrent; - -namespace Ryujinx.Ava.Ui.Controls -{ - internal class VulkanRendererControl : RendererControl - { - private const int MaxImagesInFlight = 3; - - private VulkanPlatformInterface _platformInterface; - private ConcurrentQueue _imagesInFlight; - private PresentImageInfo _currentImage; - - public VulkanRendererControl(GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel) - { - _platformInterface = AvaloniaLocator.Current.GetService(); - - _imagesInFlight = new ConcurrentQueue(); - } - - public override void DestroyBackgroundContext() - { - - } - - protected override ICustomDrawOperation CreateDrawOperation() - { - return new VulkanDrawOperation(this); - } - - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnDetachedFromVisualTree(e); - - _imagesInFlight.Clear(); - - if (_platformInterface.MainSurface.Display != null) - { - _platformInterface.MainSurface.Display.Presented -= Window_Presented; - } - - _currentImage?.Put(); - _currentImage = null; - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - - _platformInterface.MainSurface.Display.Presented += Window_Presented; - } - - private void Window_Presented(object sender, EventArgs e) - { - _platformInterface.MainSurface.Device.QueueWaitIdle(); - _currentImage?.Put(); - _currentImage = null; - } - - public override void Render(DrawingContext context) - { - base.Render(context); - } - - protected override void CreateWindow() - { - } - - internal override void MakeCurrent() - { - } - - internal override void MakeCurrent(SwappableNativeWindowBase window) - { - } - - internal override void Present(object image) - { - Image = image; - - _imagesInFlight.Enqueue((PresentImageInfo)image); - - if (_imagesInFlight.Count > MaxImagesInFlight) - { - _imagesInFlight.TryDequeue(out _); - } - - Dispatcher.UIThread.Post(InvalidateVisual); - } - - private PresentImageInfo GetImage() - { - lock (_imagesInFlight) - { - if (!_imagesInFlight.TryDequeue(out _currentImage)) - { - _currentImage = (PresentImageInfo)Image; - } - - return _currentImage; - } - } - - private class VulkanDrawOperation : ICustomDrawOperation - { - public Rect Bounds { get; } - - private readonly VulkanRendererControl _control; - private bool _isDestroyed; - - public VulkanDrawOperation(VulkanRendererControl control) - { - _control = control; - Bounds = _control.Bounds; - } - - public void Dispose() - { - if (_isDestroyed) - { - return; - } - - _isDestroyed = true; - } - - public bool Equals(ICustomDrawOperation other) - { - return other is VulkanDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds; - } - - public bool HitTest(Point p) - { - return Bounds.Contains(p); - } - - public unsafe void Render(IDrawingContextImpl context) - { - if (_isDestroyed || _control.Image == null || _control.RenderSize.Width == 0 || _control.RenderSize.Height == 0 || - context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) - { - return; - } - - var image = _control.GetImage(); - - if (!image.State.IsValid) - { - _control._currentImage = null; - - return; - } - - var gpu = AvaloniaLocator.Current.GetService(); - - image.Get(); - - var imageInfo = new GRVkImageInfo() - { - CurrentQueueFamily = _control._platformInterface.PhysicalDevice.QueueFamilyIndex, - Format = (uint)Format.R8G8B8A8Unorm, - Image = image.Image.Handle, - ImageLayout = (uint)ImageLayout.TransferSrcOptimal, - ImageTiling = (uint)ImageTiling.Optimal, - ImageUsageFlags = (uint)(ImageUsageFlags.ImageUsageColorAttachmentBit - | ImageUsageFlags.ImageUsageTransferSrcBit - | ImageUsageFlags.ImageUsageTransferDstBit), - LevelCount = 1, - SampleCount = 1, - Protected = false, - Alloc = new GRVkAlloc() - { - Memory = image.Memory.Handle, - Flags = 0, - Offset = image.MemoryOffset, - Size = image.MemorySize - } - }; - - using var backendTexture = new GRBackendRenderTarget( - (int)image.Extent.Width, - (int)image.Extent.Height, - 1, - imageInfo); - - var vulkan = AvaloniaLocator.Current.GetService(); - - using var surface = SKSurface.Create( - skiaDrawingContextImpl.GrContext, - backendTexture, - GRSurfaceOrigin.TopLeft, - SKColorType.Rgba8888); - - if (surface == null) - { - return; - } - - var rect = new Rect(new Point(), new Size(image.Extent.Width, image.Extent.Height)); - - using var snapshot = surface.Snapshot(); - skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), - new SKPaint()); - } - } - } -} diff --git a/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs b/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs new file mode 100644 index 000000000..124536d99 --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs @@ -0,0 +1,113 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Ava.Ui.Controls +{ + [SupportedOSPlatform("windows")] + internal class Win32NativeInterop + { + [Flags] + public enum ClassStyles : uint + { + CS_CLASSDC = 0x40, + CS_OWNDC = 0x20, + } + + [Flags] + public enum WindowStyles : uint + { + WS_CHILD = 0x40000000 + } + + public enum Cursors : uint + { + IDC_ARROW = 32512 + } + + public enum WindowsMessages : uint + { + MOUSEMOVE = 0x0200, + LBUTTONDOWN = 0x0201, + LBUTTONUP = 0x0202, + LBUTTONDBLCLK = 0x0203, + RBUTTONDOWN = 0x0204, + RBUTTONUP = 0x0205, + RBUTTONDBLCLK = 0x0206, + MBUTTONDOWN = 0x0207, + MBUTTONUP = 0x0208, + MBUTTONDBLCLK = 0x0209, + MOUSEWHEEL = 0x020A, + XBUTTONDOWN = 0x020B, + XBUTTONUP = 0x020C, + XBUTTONDBLCLK = 0x020D, + MOUSEHWHEEL = 0x020E, + MOUSELAST = 0x020E + } + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WNDCLASSEX + { + public int cbSize; + public ClassStyles style; + [MarshalAs(UnmanagedType.FunctionPtr)] + public WindowProc lpfnWndProc; // not WndProc + public int cbClsExtra; + public int cbWndExtra; + public IntPtr hInstance; + public IntPtr hIcon; + public IntPtr hCursor; + public IntPtr hbrBackground; + [MarshalAs(UnmanagedType.LPWStr)] + public string lpszMenuName; + [MarshalAs(UnmanagedType.LPWStr)] + public string lpszClassName; + public IntPtr hIconSm; + + public static WNDCLASSEX Create() + { + return new WNDCLASSEX + { + cbSize = Marshal.SizeOf() + }; + } + } + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern ushort RegisterClassEx(ref WNDCLASSEX param); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyWindow(IntPtr hwnd); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateWindowEx( + uint dwExStyle, + string lpClassName, + string lpWindowName, + WindowStyles dwStyle, + int x, + int y, + int nWidth, + int nHeight, + IntPtr hWndParent, + IntPtr hMenu, + IntPtr hInstance, + IntPtr lpParam); + } +} diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs index 32f08ff93..10dd2da34 100644 --- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs @@ -10,7 +10,6 @@ using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.Ui.Controls; -using Ryujinx.Ava.Ui.Vulkan; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -252,34 +251,19 @@ namespace Ryujinx.Ava.Ui.ViewModels { _gpuIds = new List(); List names = new List(); - if (!Program.UseVulkan) - { - var devices = VulkanRenderer.GetPhysicalDevices(); + var devices = VulkanRenderer.GetPhysicalDevices(); - if (devices.Length == 0) - { - IsVulkanAvailable = false; - GraphicsBackendIndex = 1; - } - else - { - foreach (var device in devices) - { - _gpuIds.Add(device.Id); - names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}"); - } - } + if (devices.Length == 0) + { + IsVulkanAvailable = false; + GraphicsBackendIndex = 1; } else { - foreach (var device in VulkanPhysicalDevice.SuitableDevices) + foreach (var device in devices) { - _gpuIds.Add( - VulkanInitialization.StringFromIdPair(device.Value.VendorID, device.Value.DeviceID)); - var value = device.Value; - var name = value.DeviceName; - names.Add( - $"{Marshal.PtrToStringAnsi((IntPtr)name)} {(device.Value.DeviceType == PhysicalDeviceType.DiscreteGpu ? "(dGPU)" : "")}"); + _gpuIds.Add(device.Id); + names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}"); } } @@ -407,7 +391,7 @@ namespace Ryujinx.Ava.Ui.ViewModels _previousVolumeLevel = Volume; } - public async Task SaveSettings() + public void SaveSettings() { ConfigurationState config = ConfigurationState.Instance; @@ -422,8 +406,6 @@ namespace Ryujinx.Ava.Ui.ViewModels config.System.TimeZone.Value = TimeZone; } - bool requiresRestart = config.Graphics.GraphicsBackend.Value != (GraphicsBackend)GraphicsBackendIndex; - config.Logger.EnableError.Value = EnableError; config.Logger.EnableTrace.Value = EnableTrace; config.Logger.EnableWarn.Value = EnableWarn; @@ -456,19 +438,7 @@ namespace Ryujinx.Ava.Ui.ViewModels config.System.Language.Value = (Language)Language; config.System.Region.Value = (Region)Region; - var selectedGpu = _gpuIds.ElementAtOrDefault(PreferredGpuIndex); - if (!requiresRestart) - { - var platform = AvaloniaLocator.Current.GetService(); - if (platform != null) - { - var physicalDevice = platform.PhysicalDevice; - - requiresRestart = physicalDevice.DeviceId != selectedGpu; - } - } - - config.Graphics.PreferredGpu.Value = selectedGpu; + config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex); if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex) { @@ -507,20 +477,6 @@ namespace Ryujinx.Ava.Ui.ViewModels MainWindow.UpdateGraphicsConfig(); _previousVolumeLevel = Volume; - - if (requiresRestart) - { - var choice = await ContentDialogHelper.CreateChoiceDialog( - LocaleManager.Instance["SettingsAppRequiredRestartMessage"], - LocaleManager.Instance["SettingsGpuBackendRestartMessage"], - LocaleManager.Instance["SettingsGpuBackendRestartSubMessage"]); - - if (choice) - { - Process.Start(Environment.ProcessPath); - Environment.Exit(0); - } - } } public void RevertIfNotSaved() diff --git a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml new file mode 100644 index 000000000..2967f4f21 --- /dev/null +++ b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs new file mode 100644 index 000000000..7a51e64d0 --- /dev/null +++ b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media; + +namespace Ryujinx.Ava.Ui.Windows +{ + public partial class ContentDialogOverlayWindow : StyleableWindow + { + public ContentDialogOverlayWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + ExtendClientAreaToDecorationsHint = true; + TransparencyLevelHint = WindowTransparencyLevel.Transparent; + WindowStartupLocation = WindowStartupLocation.Manual; + SystemDecorations = SystemDecorations.None; + ExtendClientAreaTitleBarHeightHint = 0; + Background = Brushes.Transparent; + CanResize = false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml index 610281a60..6eafd5e50 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml @@ -462,6 +462,7 @@ VerticalAlignment="Stretch" Background="{DynamicResource ThemeContentBackgroundColor}" IsVisible="{Binding ShowLoadProgress}" + Name="LoadingView" ZIndex="1000"> @@ -346,17 +355,17 @@ namespace Ryujinx.Ava.Ui.Windows Dispatcher.UIThread.InvokeAsync(() => { - if (MainContent.Content != _mainViewContent) - { - MainContent.Content = _mainViewContent; - } - ViewModel.ShowMenuAndStatusBar = true; ViewModel.ShowContent = true; ViewModel.ShowLoadProgress = false; ViewModel.IsLoadingIndeterminate = false; Cursor = Cursor.Default; + if (MainContent.Content != _mainViewContent) + { + MainContent.Content = _mainViewContent; + } + AppHost = null; HandleRelaunch(); diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs index 5b2ea276c..73ac06242 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs @@ -211,9 +211,9 @@ namespace Ryujinx.Ava.Ui.Windows } } - private async void SaveButton_Clicked(object sender, RoutedEventArgs e) + private void SaveButton_Clicked(object sender, RoutedEventArgs e) { - await SaveSettings(); + SaveSettings(); Close(); } @@ -224,14 +224,14 @@ namespace Ryujinx.Ava.Ui.Windows Close(); } - private async void ApplyButton_Clicked(object sender, RoutedEventArgs e) + private void ApplyButton_Clicked(object sender, RoutedEventArgs e) { - await SaveSettings(); + SaveSettings(); } - private async Task SaveSettings() + private void SaveSettings() { - await ViewModel.SaveSettings(); + ViewModel.SaveSettings(); ControllerSettings?.SaveCurrentProfile(); diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/Ryujinx.Graphics.GAL/IWindow.cs index 043193c9c..a9bbbc5e0 100644 --- a/Ryujinx.Graphics.GAL/IWindow.cs +++ b/Ryujinx.Graphics.GAL/IWindow.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL { public interface IWindow { - void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); + void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); void SetSize(int width, int height); diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs index f0fec1733..c4f3b553a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs @@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window public CommandType CommandType => CommandType.WindowPresent; private TableRef _texture; private ImageCrop _crop; - private TableRef> _swapBuffersCallback; + private TableRef _swapBuffersCallback; - public void Set(TableRef texture, ImageCrop crop, TableRef> swapBuffersCallback) + public void Set(TableRef texture, ImageCrop crop, TableRef swapBuffersCallback) { _texture = texture; _crop = crop; diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index 21a66e7cb..c4b62a25d 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -16,13 +16,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading _impl = impl; } - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { // If there's already a frame in the pipeline, wait for it to be presented first. // This is a multithread rate limit - we can't be more than one frame behind the command queue. _renderer.WaitForFrame(); - _renderer.New().Set(new TableRef(_renderer, texture as ThreadedTexture), crop, new TableRef>(_renderer, swapBuffersCallback)); + _renderer.New().Set(new TableRef(_renderer, texture as ThreadedTexture), crop, new TableRef(_renderer, swapBuffersCallback)); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index 922583cf5..8ad70c7f1 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.Gpu /// If the queue is empty, then no texture is presented. /// /// Callback method to call when a new texture should be presented on the screen - public void Present(Action swapBuffersCallback) + public void Present(Action swapBuffersCallback) { _context.AdvanceSequence(); diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index edebf1a09..61b739b11 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -12,11 +12,7 @@ namespace Ryujinx.Graphics.OpenGL private int _width; private int _height; - private bool _sizeChanged; private int _copyFramebufferHandle; - private int _stagingFrameBuffer; - private int[] _stagingTextures; - private int _currentTexture; internal BackgroundContextWorker BackgroundContext { get; private set; } @@ -25,28 +21,15 @@ namespace Ryujinx.Graphics.OpenGL public Window(OpenGLRenderer renderer) { _renderer = renderer; - _stagingTextures = new int[TextureCount]; } - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { GL.Disable(EnableCap.FramebufferSrgb); - if (_sizeChanged) - { - if (_stagingFrameBuffer != 0) - { - GL.DeleteTextures(_stagingTextures.Length, _stagingTextures); - GL.DeleteFramebuffer(_stagingFrameBuffer); - } - - CreateStagingFramebuffer(); - _sizeChanged = false; - } - (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); - CopyTextureToFrameBufferRGB(_stagingFrameBuffer, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback); + CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); @@ -59,41 +42,17 @@ namespace Ryujinx.Graphics.OpenGL public void ChangeVSyncMode(bool vsyncEnabled) { } - private void CreateStagingFramebuffer() - { - _stagingFrameBuffer = GL.GenFramebuffer(); - GL.GenTextures(_stagingTextures.Length, _stagingTextures); - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, _stagingFrameBuffer); - - foreach (var stagingTexture in _stagingTextures) - { - GL.BindTexture(TextureTarget.Texture2D, stagingTexture); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, _width, _height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, stagingTexture, 0); - } - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - GL.BindTexture(TextureTarget.Texture2D, 0); - } - public void SetSize(int width, int height) { _width = width; _height = height; - _sizeChanged = true; } - private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback) + private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback) { GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); - GL.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _stagingTextures[_currentTexture], 0); - TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view; GL.FramebufferTexture( @@ -189,12 +148,8 @@ namespace Ryujinx.Graphics.OpenGL // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); GL.Viewport(0, 0, _width, _height); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer); - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _stagingFrameBuffer); - - swapBuffersCallback((object)_stagingTextures[_currentTexture]); - _currentTexture = ++_currentTexture % _stagingTextures.Length; + swapBuffersCallback(); ((Pipeline)_renderer.Pipeline).RestoreClipControl(); ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); @@ -246,14 +201,6 @@ namespace Ryujinx.Graphics.OpenGL _copyFramebufferHandle = 0; } - - if (_stagingFrameBuffer != 0) - { - GL.DeleteTextures(_stagingTextures.Length, _stagingTextures); - GL.DeleteFramebuffer(_stagingFrameBuffer); - _stagingFrameBuffer = 0; - _stagingTextures = null; - } } } } diff --git a/Ryujinx.Graphics.Vulkan/ImageWindow.cs b/Ryujinx.Graphics.Vulkan/ImageWindow.cs deleted file mode 100644 index 69302fdf7..000000000 --- a/Ryujinx.Graphics.Vulkan/ImageWindow.cs +++ /dev/null @@ -1,429 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Silk.NET.Vulkan; -using System; -using VkFormat = Silk.NET.Vulkan.Format; - -namespace Ryujinx.Graphics.Vulkan -{ - class ImageWindow : WindowBase, IWindow, IDisposable - { - internal const VkFormat Format = VkFormat.R8G8B8A8Unorm; - - private const int ImageCount = 3; - private const int SurfaceWidth = 1280; - private const int SurfaceHeight = 720; - - private readonly VulkanRenderer _gd; - private readonly PhysicalDevice _physicalDevice; - private readonly Device _device; - - private Auto[] _images; - private Auto[] _imageViews; - private Auto[] _imageAllocationAuto; - private ImageState[] _states; - private PresentImageInfo[] _presentedImages; - private FenceHolder[] _fences; - - private ulong[] _imageSizes; - private ulong[] _imageOffsets; - - private int _width = SurfaceWidth; - private int _height = SurfaceHeight; - private bool _recreateImages; - private int _nextImage; - - public unsafe ImageWindow(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) - { - _gd = gd; - _physicalDevice = physicalDevice; - _device = device; - - _images = new Auto[ImageCount]; - _imageAllocationAuto = new Auto[ImageCount]; - _imageSizes = new ulong[ImageCount]; - _imageOffsets = new ulong[ImageCount]; - _states = new ImageState[ImageCount]; - _presentedImages = new PresentImageInfo[ImageCount]; - - CreateImages(); - } - - private void RecreateImages() - { - for (int i = 0; i < ImageCount; i++) - { - lock (_states[i]) - { - _states[i].IsValid = false; - _fences[i]?.Wait(); - _fences[i]?.Put(); - _imageViews[i]?.Dispose(); - _imageAllocationAuto[i]?.Dispose(); - _images[i]?.Dispose(); - } - } - _presentedImages = null; - - CreateImages(); - } - - private unsafe void CreateImages() - { - _imageViews = new Auto[ImageCount]; - _fences = new FenceHolder[ImageCount]; - _presentedImages = new PresentImageInfo[ImageCount]; - - _nextImage = 0; - var cbs = _gd.CommandBufferPool.Rent(); - - var imageCreateInfo = new ImageCreateInfo - { - SType = StructureType.ImageCreateInfo, - ImageType = ImageType.ImageType2D, - Format = Format, - Extent = new Extent3D((uint?)_width, (uint?)_height, 1), - MipLevels = 1, - ArrayLayers = 1, - Samples = SampleCountFlags.SampleCount1Bit, - Tiling = ImageTiling.Optimal, - Usage = ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageTransferDstBit, - SharingMode = SharingMode.Exclusive, - InitialLayout = ImageLayout.Undefined, - Flags = ImageCreateFlags.ImageCreateMutableFormatBit - }; - - for (int i = 0; i < _images.Length; i++) - { - _gd.Api.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError(); - _images[i] = new Auto(new DisposableImage(_gd.Api, _device, image)); - - _gd.Api.GetImageMemoryRequirements(_device, image, - out var memoryRequirements); - var allocation = _gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, memoryRequirements, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit); - - _imageSizes[i] = allocation.Size; - _imageOffsets[i] = allocation.Offset; - - _imageAllocationAuto[i] = new Auto(allocation); - - _gd.Api.BindImageMemory(_device, image, allocation.Memory, allocation.Offset); - - _imageViews[i] = CreateImageView(image, Format); - - Transition( - cbs.CommandBuffer, - image, - 0, - 0, - ImageLayout.Undefined, - ImageLayout.TransferSrcOptimal); - - _states[i] = new ImageState(); - } - - _gd.CommandBufferPool.Return(cbs); - } - - private unsafe Auto CreateImageView(Image image, VkFormat format) - { - var componentMapping = new ComponentMapping( - ComponentSwizzle.R, - ComponentSwizzle.G, - ComponentSwizzle.B, - ComponentSwizzle.A); - - var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1); - - var imageCreateInfo = new ImageViewCreateInfo() - { - SType = StructureType.ImageViewCreateInfo, - Image = image, - ViewType = ImageViewType.ImageViewType2D, - Format = format, - Components = componentMapping, - SubresourceRange = subresourceRange - }; - - _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError(); - return new Auto(new DisposableImageView(_gd.Api, _device, imageView)); - } - - public override unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) - { - if (_recreateImages) - { - RecreateImages(); - _recreateImages = false; - } - - var image = _images[_nextImage]; - - _gd.FlushAllCommands(); - - var cbs = _gd.CommandBufferPool.Rent(); - - Transition( - cbs.CommandBuffer, - image.GetUnsafe().Value, - 0, - AccessFlags.AccessTransferWriteBit, - ImageLayout.TransferSrcOptimal, - ImageLayout.General); - - var view = (TextureView)texture; - - int srcX0, srcX1, srcY0, srcY1; - float scale = view.ScaleFactor; - - if (crop.Left == 0 && crop.Right == 0) - { - srcX0 = 0; - srcX1 = (int)(view.Width / scale); - } - else - { - srcX0 = crop.Left; - srcX1 = crop.Right; - } - - if (crop.Top == 0 && crop.Bottom == 0) - { - srcY0 = 0; - srcY1 = (int)(view.Height / scale); - } - else - { - srcY0 = crop.Top; - srcY1 = crop.Bottom; - } - - if (scale != 1f) - { - srcX0 = (int)(srcX0 * scale); - srcY0 = (int)(srcY0 * scale); - srcX1 = (int)Math.Ceiling(srcX1 * scale); - srcY1 = (int)Math.Ceiling(srcY1 * scale); - } - - if (ScreenCaptureRequested) - { - CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY); - - ScreenCaptureRequested = false; - } - - float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY)); - float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX)); - - int dstWidth = (int)(_width * ratioX); - int dstHeight = (int)(_height * ratioY); - - int dstPaddingX = (_width - dstWidth) / 2; - int dstPaddingY = (_height - dstHeight) / 2; - - int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; - int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; - - int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; - int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - - _gd.HelperShader.Blit( - _gd, - cbs, - view, - _imageViews[_nextImage], - _width, - _height, - Format, - new Extents2D(srcX0, srcY0, srcX1, srcY1), - new Extents2D(dstX0, dstY1, dstX1, dstY0), - true, - true); - - Transition( - cbs.CommandBuffer, - image.GetUnsafe().Value, - 0, - 0, - ImageLayout.General, - ImageLayout.TransferSrcOptimal); - - _gd.CommandBufferPool.Return( - cbs, - null, - stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit }, - null); - - _fences[_nextImage]?.Put(); - _fences[_nextImage] = cbs.GetFence(); - cbs.GetFence().Get(); - - PresentImageInfo info = _presentedImages[_nextImage]; - - if (info == null) - { - info = new PresentImageInfo( - image, - _imageAllocationAuto[_nextImage], - _device, - _physicalDevice, - _imageSizes[_nextImage], - _imageOffsets[_nextImage], - new Extent2D((uint)_width, (uint)_height), - _states[_nextImage]); - - _presentedImages[_nextImage] = info; - } - - swapBuffersCallback(info); - - _nextImage = (_nextImage + 1) % ImageCount; - } - - private unsafe void Transition( - CommandBuffer commandBuffer, - Image image, - AccessFlags srcAccess, - AccessFlags dstAccess, - ImageLayout srcLayout, - ImageLayout dstLayout) - { - var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1); - - var barrier = new ImageMemoryBarrier() - { - SType = StructureType.ImageMemoryBarrier, - SrcAccessMask = srcAccess, - DstAccessMask = dstAccess, - OldLayout = srcLayout, - NewLayout = dstLayout, - SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, - DstQueueFamilyIndex = Vk.QueueFamilyIgnored, - Image = image, - SubresourceRange = subresourceRange - }; - - _gd.Api.CmdPipelineBarrier( - commandBuffer, - PipelineStageFlags.PipelineStageTopOfPipeBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - 0, - 0, - null, - 0, - null, - 1, - barrier); - } - - private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) - { - byte[] bitmap = texture.GetData(x, y, width, height); - - _gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY)); - } - - public override void SetSize(int width, int height) - { - if (_width != width || _height != height) - { - _recreateImages = true; - } - - _width = width; - _height = height; - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - unsafe - { - for (int i = 0; i < ImageCount; i++) - { - _states[i].IsValid = false; - _fences[i]?.Wait(); - _fences[i]?.Put(); - _imageViews[i]?.Dispose(); - _imageAllocationAuto[i]?.Dispose(); - _images[i]?.Dispose(); - } - } - } - } - - public override void Dispose() - { - Dispose(true); - } - - public override void ChangeVSyncMode(bool vsyncEnabled) { } - } - - public class ImageState - { - private bool _isValid = true; - - public bool IsValid - { - get => _isValid; - internal set - { - _isValid = value; - - StateChanged?.Invoke(this, _isValid); - } - } - - public event EventHandler StateChanged; - } - - public class PresentImageInfo - { - private readonly Auto _image; - private readonly Auto _memory; - - public Image Image => _image.GetUnsafe().Value; - - public DeviceMemory Memory => _memory.GetUnsafe().Memory; - - public Device Device { get; } - public PhysicalDevice PhysicalDevice { get; } - public ulong MemorySize { get; } - public ulong MemoryOffset { get; } - public Extent2D Extent { get; } - public ImageState State { get; internal set; } - internal PresentImageInfo( - Auto image, - Auto memory, - Device device, - PhysicalDevice physicalDevice, - ulong memorySize, - ulong memoryOffset, - Extent2D extent2D, - ImageState state) - { - _image = image; - _memory = memory; - Device = device; - PhysicalDevice = physicalDevice; - MemorySize = memorySize; - MemoryOffset = memoryOffset; - Extent = extent2D; - State = state; - } - - public void Get() - { - _memory.IncrementReferenceCount(); - _image.IncrementReferenceCount(); - } - - public void Put() - { - _memory.DecrementReferenceCount(); - _image.DecrementReferenceCount(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 5abe1be15..3776be9ee 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Vulkan private SurfaceKHR _surface; private PhysicalDevice _physicalDevice; private Device _device; - private uint _queueFamilyIndex; private WindowBase _window; internal FormatCapabilities FormatCapabilities { get; private set; } @@ -37,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan internal ExtDebugReport DebugReportApi { get; private set; } internal uint QueueFamilyIndex { get; private set; } - public bool IsOffScreen { get; } internal Queue Queue { get; private set; } internal Queue BackgroundQueue { get; private set; } internal object BackgroundQueueLock { get; private set; } @@ -94,22 +92,6 @@ namespace Ryujinx.Graphics.Vulkan Samplers = new HashSet(); } - public VulkanRenderer(Instance instance, Device device, PhysicalDevice physicalDevice, Queue queue, uint queueFamilyIndex, object lockObject) - { - _instance = instance; - _physicalDevice = physicalDevice; - _device = device; - _queueFamilyIndex = queueFamilyIndex; - - Queue = queue; - QueueLock = lockObject; - - IsOffScreen = true; - Shaders = new HashSet(); - Textures = new HashSet(); - Samplers = new HashSet(); - } - private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) { FormatCapabilities = new FormatCapabilities(Api, _physicalDevice); @@ -286,34 +268,6 @@ namespace Ryujinx.Graphics.Vulkan _window = new Window(this, _surface, _physicalDevice, _device); } - private unsafe void SetupOffScreenContext(GraphicsDebugLevel logLevel) - { - var api = Vk.GetApi(); - - Api = api; - - VulkanInitialization.CreateDebugCallbacks(api, logLevel, _instance, out var debugReport, out _debugReportCallback); - - DebugReportApi = debugReport; - - var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice); - - uint propertiesCount; - - api.GetPhysicalDeviceQueueFamilyProperties(_physicalDevice, &propertiesCount, null); - - QueueFamilyProperties[] queueFamilyProperties = new QueueFamilyProperties[propertiesCount]; - - fixed (QueueFamilyProperties* pProperties = queueFamilyProperties) - { - api.GetPhysicalDeviceQueueFamilyProperties(_physicalDevice, &propertiesCount, pProperties); - } - - LoadFeatures(supportedExtensions, queueFamilyProperties[0].QueueCount, _queueFamilyIndex); - - _window = new ImageWindow(this, _physicalDevice, _device); - } - public BufferHandle CreateBuffer(int size) { return BufferManager.CreateWithHandle(this, size, false); @@ -519,14 +473,7 @@ namespace Ryujinx.Graphics.Vulkan public void Initialize(GraphicsDebugLevel logLevel) { - if (IsOffScreen) - { - SetupOffScreenContext(logLevel); - } - else - { - SetupContext(logLevel); - } + SetupContext(logLevel); PrintGpuInformation(); } @@ -638,15 +585,12 @@ namespace Ryujinx.Graphics.Vulkan sampler.Dispose(); } - if (!IsOffScreen) - { - SurfaceApi.DestroySurface(_instance, _surface, null); + SurfaceApi.DestroySurface(_instance, _surface, null); - Api.DestroyDevice(_device, null); + Api.DestroyDevice(_device, null); - // Last step destroy the instance - Api.DestroyInstance(_instance, null); - } + // Last step destroy the instance + Api.DestroyInstance(_instance, null); } } } diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 26f53b395..71b542044 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -217,7 +217,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { uint nextImage = 0; diff --git a/Ryujinx.Graphics.Vulkan/WindowBase.cs b/Ryujinx.Graphics.Vulkan/WindowBase.cs index 80b5c0e3f..651fe7c16 100644 --- a/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan public bool ScreenCaptureRequested { get; set; } public abstract void Dispose(); - public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); + public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); public abstract void SetSize(int width, int height); public abstract void ChangeVSyncMode(bool vsyncEnabled); } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 8ea595e84..4bd1fe397 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -117,7 +117,7 @@ namespace Ryujinx.HLE return Gpu.Window.ConsumeFrameAvailable(); } - public void PresentFrame(Action swapBuffersCallback) + public void PresentFrame(Action swapBuffersCallback) { Gpu.Window.Present(swapBuffersCallback); } diff --git a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index aa2e86d9d..d1d0872b3 100644 --- a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL GL.ClearColor(0, 0, 0, 1.0f); GL.Clear(ClearBufferMask.ColorBufferBit); - SwapBuffers(0); + SwapBuffers(); Renderer?.Window.SetSize(DefaultWidth, DefaultHeight); MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); @@ -156,28 +156,8 @@ namespace Ryujinx.Headless.SDL2.OpenGL _openGLContext.Dispose(); } - protected override void SwapBuffers(object image) + protected override void SwapBuffers() { - if ((int)image != 0) - { - // The game's framebruffer is already bound, so blit it to the window's backbuffer - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); - - GL.Clear(ClearBufferMask.ColorBufferBit); - GL.ClearColor(0, 0, 0, 1); - - GL.BlitFramebuffer(0, - 0, - Width, - Height, - 0, - 0, - Width, - Height, - ClearBufferMask.ColorBufferBit, - BlitFramebufferFilter.Linear); - } - SDL_GL_SwapWindow(WindowHandle); } } diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 9ec1dc63b..0fcf517be 100644 --- a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -77,6 +77,6 @@ namespace Ryujinx.Headless.SDL2.Vulkan Device.DisposeGpu(); } - protected override void SwapBuffers(object texture) { } + protected override void SwapBuffers() { } } } diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs index cc0986a0f..2d6963dd6 100644 --- a/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/Ryujinx.Headless.SDL2/WindowBase.cs @@ -157,7 +157,7 @@ namespace Ryujinx.Headless.SDL2 protected abstract void FinalizeWindowRenderer(); - protected abstract void SwapBuffers(object image); + protected abstract void SwapBuffers(); public abstract SDL_WindowFlags GetWindowFlags(); @@ -202,7 +202,7 @@ namespace Ryujinx.Headless.SDL2 while (Device.ConsumeFrameAvailable()) { - Device.PresentFrame((texture) => { SwapBuffers(texture); }); + Device.PresentFrame(SwapBuffers); } if (_ticks >= _ticksPerFrame) diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index b839e9cd4..06d414edc 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -97,31 +97,11 @@ namespace Ryujinx.Ui GL.ClearColor(0, 0, 0, 1.0f); GL.Clear(ClearBufferMask.ColorBufferBit); - SwapBuffers(0); + SwapBuffers(); } - public override void SwapBuffers(object image) + public override void SwapBuffers() { - if((int)image != 0) - { - // The game's framebruffer is already bound, so blit it to the window's backbuffer - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); - - GL.Clear(ClearBufferMask.ColorBufferBit); - GL.ClearColor(0, 0, 0, 1); - - GL.BlitFramebuffer(0, - 0, - WindowWidth, - WindowHeight, - 0, - 0, - WindowWidth, - WindowHeight, - ClearBufferMask.ColorBufferBit, - BlitFramebufferFilter.Linear); - } - _nativeWindow.SwapBuffers(); } diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 3cdc424ef..6a728a26d 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Ui public abstract void InitializeRenderer(); - public abstract void SwapBuffers(object image); + public abstract void SwapBuffers(); protected abstract string GetGpuBackendName(); @@ -426,7 +426,7 @@ namespace Ryujinx.Ui while (Device.ConsumeFrameAvailable()) { - Device.PresentFrame((texture) => { SwapBuffers(texture);}); + Device.PresentFrame(SwapBuffers); } if (_ticks >= _ticksPerFrame) diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs index f65b330b6..49578d2af 100644 --- a/Ryujinx/Ui/VKRenderer.cs +++ b/Ryujinx/Ui/VKRenderer.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Ui public override void InitializeRenderer() { } - public override void SwapBuffers(object image) { } + public override void SwapBuffers() { } protected override string GetGpuBackendName() { From 0cb1e926b594432134f5d35d02da622cd3fd3458 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 19 Sep 2022 15:35:47 -0300 Subject: [PATCH 4/8] Allow bindless textures with handles from unbound constant buffer (#3706) --- Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs | 8 ++++++-- Ryujinx.Graphics.Shader/TextureHandle.cs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index d87721bfb..cd84024b3 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -806,7 +806,9 @@ namespace Ryujinx.Graphics.Gpu.Image ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); - int handle = _channel.MemoryManager.Physical.Read(textureBufferAddress + (uint)textureWordOffset * 4); + int handle = textureBufferAddress != 0 + ? _channel.MemoryManager.Physical.Read(textureBufferAddress + (uint)textureWordOffset * 4) + : 0; // The "wordOffset" (which is really the immediate value used on texture instructions on the shader) // is a 13-bit value. However, in order to also support separate samplers and textures (which uses @@ -824,7 +826,9 @@ namespace Ryujinx.Graphics.Gpu.Image ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex) : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex); - samplerHandle = _channel.MemoryManager.Physical.Read(samplerBufferAddress + (uint)samplerWordOffset * 4); + samplerHandle = samplerBufferAddress != 0 + ? _channel.MemoryManager.Physical.Read(samplerBufferAddress + (uint)samplerWordOffset * 4) + : 0; } else { diff --git a/Ryujinx.Graphics.Shader/TextureHandle.cs b/Ryujinx.Graphics.Shader/TextureHandle.cs index a2842bb89..39d5c1c32 100644 --- a/Ryujinx.Graphics.Shader/TextureHandle.cs +++ b/Ryujinx.Graphics.Shader/TextureHandle.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader { (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset); - int handle = cachedTextureBuffer[textureWordOffset]; + int handle = cachedTextureBuffer.Length != 0 ? cachedTextureBuffer[textureWordOffset] : 0; // The "wordOffset" (which is really the immediate value used on texture instructions on the shader) // is a 13-bit value. However, in order to also support separate samplers and textures (which uses @@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader if (handleType != TextureHandleType.SeparateConstantSamplerHandle) { - samplerHandle = cachedSamplerBuffer[samplerWordOffset]; + samplerHandle = cachedSamplerBuffer.Length != 0 ? cachedSamplerBuffer[samplerWordOffset] : 0; } else { From 41790aa7434a5e6d132fad2e24844d450378205b Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:04:22 +0100 Subject: [PATCH 5/8] Avalonia - Misc changes to UX (#3643) * Change navbar from compact to default and force text overflow globally * Fix settings window * Fix right stick control alignment * Initialize value and add logging for SDL IDs * Fix alignment of setting text and improve borders * Clean up padding and size of buttons on controller settings * Fix right side trigger alignment and correct styling * Revert axaml alignment * Fix alignment of volume widget * Fix timezone autocompletebox dropdown height * MainWindow: Line up volume status bar item * Remove margins and add padding to volume widget * Make volume text localizable. Co-authored-by: merry --- Ryujinx.Ava/AppHost.cs | 2 +- Ryujinx.Ava/Assets/Locales/en_US.json | 3 +- Ryujinx.Ava/Assets/Styles/Styles.xaml | 14 ++ .../Ui/Models/StatusUpdatedEventArgs.cs | 6 +- .../ViewModels/ControllerSettingsViewModel.cs | 24 +++- .../Ui/ViewModels/MainWindowViewModel.cs | 8 +- .../Ui/Windows/ControllerSettingsWindow.axaml | 33 ++--- Ryujinx.Ava/Ui/Windows/MainWindow.axaml | 8 +- Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 1 + Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml | 120 ++++++++++-------- 10 files changed, 133 insertions(+), 86 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 7cf5934a6..320efeb5e 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -879,7 +879,7 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, - Device.GetVolume(), + LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%", Renderer.IsVulkan ? "Vulkan" : "OpenGL", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 350e20635..a6641a028 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -588,5 +588,6 @@ "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?" + "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", + "VolumeShort": "Vol" } diff --git a/Ryujinx.Ava/Assets/Styles/Styles.xaml b/Ryujinx.Ava/Assets/Styles/Styles.xaml index c93640ae9..8b09bafdd 100644 --- a/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -54,6 +54,12 @@ + @@ -193,6 +199,14 @@ + + +