Compare commits

..

11 Commits

Author SHA1 Message Date
c6d82209ab Restride vertex buffer when stride causes attributes to misalign in Vulkan. (#3679)
* Vertex Buffer Alignment part 1

* Update CacheByRange

* Add Stride Change compute shader, fix storage buffers in helpers

* An AMD exclusive

* Reword

* Change rules - stride conversion when attrs misalign

* Fix stupid mistake

* Fix background pipeline compile

* Improve a few things.

* Fix some feedback

* Address Feedback

(the shader binary didn't change when i changed the source to use the subgroup size)

* Fix bug where rewritten buffer would be disposed instantly.
2022-09-08 20:30:19 -03:00
ee1825219b Clean up rejit queue (#2751) 2022-09-08 20:14:08 -03:00
7baa08dcb4 Implemented in IR the managed methods of the Saturating region ... (#3665)
* 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.
2022-09-08 19:40:41 -03:00
408bd63b08 Transform shader LDC into constant buffer access if offset is constant (#3672)
* Transform shader LDC into constant buffer access if offset is constant

* Shader cache version bump
2022-09-07 20:25:22 -03:00
df99257d7f bsd: improve socket poll
We should report errors even when not requested.

This also ensure we only clear the bits that were requested on the output.

Finally, this fix when input events is 0.
2022-09-07 22:58:41 +02:00
f3835dc78b bsd: implement SendMMsg and RecvMMsg (#3660)
* bsd: implement sendmmsg and recvmmsg

* Fix wrong increment of vlen
2022-09-07 22:37:15 +02:00
51bb8707ef Update bug report template (#3676)
Adds some verbiage to indicate that game-specific issues should be posted instead on the game compatibility list, unless it is a provable regression.
2022-09-06 22:30:07 +02:00
5ff5fe47ba Bsd: Fix NullReferenceException in BsdSockAddr.FromIPEndPoint() (#3652)
* Bsd: Fix NullReferenceException in BsdSockAddr.FromIPEndPoint()

Allows "Victor Vran Overkill Edition" to boot with guest internet access enabled.
Thanks to EmulationFanatic for testing this for me!

* Bsd: Return proper error code if RemoteEndPoint is null

* Remove whitespace from empty line

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-09-01 22:04:01 +00:00
38275f9056 Change vsync signal to happen at 60hz, regardless of swap interval (#3642)
* Change vsync signal to happen at 60hz, regardless of swap interval

* Update Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix softlock when toggling vsync

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-09-01 17:57:50 -03:00
67cbdc3a6a bsd: Fix Poll(0) returning ETIMEDOUT instead of SUCCESS
This was an oversight of the implementation.
2022-09-01 21:46:11 +02:00
131b43170e sfdsnres: fix endianess issue for port serialisation 2022-09-01 21:31:20 +02:00
40 changed files with 2101 additions and 586 deletions

View File

@ -1,6 +1,6 @@
---
name: Bug Report
about: Something doesn't work correctly in Ryujinx.
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
#assignees:
---

View File

@ -1,4 +1,4 @@
using ARMeilleure.Diagnostics.EventSources;
using ARMeilleure.Diagnostics;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
@ -218,7 +218,7 @@ namespace ARMeilleure.Common
_pages.Add(page);
AddressTableEventSource.Log.Allocated(size, leaf);
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
return page;
}

View File

@ -1,51 +0,0 @@
using System.Diagnostics.Tracing;
namespace ARMeilleure.Diagnostics.EventSources
{
[EventSource(Name = "ARMeilleure")]
class AddressTableEventSource : EventSource
{
public static readonly AddressTableEventSource Log = new();
private ulong _size;
private ulong _leafSize;
private PollingCounter _sizeCounter;
private PollingCounter _leafSizeCounter;
public AddressTableEventSource()
{
_sizeCounter = new PollingCounter("addr-tab-alloc", this, () => _size / 1024d / 1024d)
{
DisplayName = "AddressTable Total Bytes Allocated",
DisplayUnits = "MB"
};
_leafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _leafSize / 1024d / 1024d)
{
DisplayName = "AddressTable Total Leaf Bytes Allocated",
DisplayUnits = "MB"
};
}
public void Allocated(int bytes, bool leaf)
{
_size += (uint)bytes;
if (leaf)
{
_leafSize += (uint)bytes;
}
}
protected override void Dispose(bool disposing)
{
_leafSizeCounter.Dispose();
_leafSizeCounter = null;
_sizeCounter.Dispose();
_sizeCounter = null;
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,67 @@
using System.Diagnostics.Tracing;
using System.Threading;
namespace ARMeilleure.Diagnostics
{
[EventSource(Name = "ARMeilleure")]
class TranslatorEventSource : EventSource
{
public static readonly TranslatorEventSource Log = new();
private int _rejitQueue;
private ulong _funcTabSize;
private ulong _funcTabLeafSize;
private PollingCounter _rejitQueueCounter;
private PollingCounter _funcTabSizeCounter;
private PollingCounter _funcTabLeafSizeCounter;
public TranslatorEventSource()
{
_rejitQueueCounter = new PollingCounter("rejit-queue-length", this, () => _rejitQueue)
{
DisplayName = "Rejit Queue Length"
};
_funcTabSizeCounter = new PollingCounter("addr-tab-alloc", this, () => _funcTabSize / 1024d / 1024d)
{
DisplayName = "AddressTable Total Bytes Allocated",
DisplayUnits = "MB"
};
_funcTabLeafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _funcTabLeafSize / 1024d / 1024d)
{
DisplayName = "AddressTable Total Leaf Bytes Allocated",
DisplayUnits = "MB"
};
}
public void RejitQueueAdd(int count)
{
Interlocked.Add(ref _rejitQueue, count);
}
public void AddressTableAllocated(int bytes, bool leaf)
{
_funcTabSize += (uint)bytes;
if (leaf)
{
_funcTabLeafSize += (uint)bytes;
}
}
protected override void Dispose(bool disposing)
{
_rejitQueueCounter.Dispose();
_rejitQueueCounter = null;
_funcTabLeafSizeCounter.Dispose();
_funcTabLeafSizeCounter = null;
_funcTabSizeCounter.Dispose();
_funcTabSizeCounter = null;
base.Dispose(disposing);
}
}
}

View File

@ -1352,7 +1352,7 @@ namespace ARMeilleure.Instructions
if (op.Size <= 2)
{
de = EmitSatQ(context, emit(ne), op.Size, signedSrc: true, signedDst: true);
de = EmitSignedSrcSatQ(context, emit(ne), op.Size, signedDst: true);
}
else /* if (op.Size == 3) */
{
@ -1419,15 +1419,18 @@ namespace ARMeilleure.Instructions
{
Operand temp = add ? context.Add(ne, me) : context.Subtract(ne, me);
de = EmitSatQ(context, temp, op.Size, signedSrc: true, signedDst: signed);
de = EmitSignedSrcSatQ(context, temp, op.Size, signedDst: signed);
}
else if (add) /* if (op.Size == 3) */
else /* if (op.Size == 3) */
{
de = EmitBinarySatQAdd(context, ne, me, signed);
if (add)
{
de = signed ? EmitBinarySignedSatQAdd(context, ne, me) : EmitBinaryUnsignedSatQAdd(context, ne, me);
}
else /* if (sub) */
{
de = EmitBinarySatQSub(context, ne, me, signed);
de = signed ? EmitBinarySignedSatQSub(context, ne, me) : EmitBinaryUnsignedSatQSub(context, ne, me);
}
}
res = EmitVectorInsert(context, res, de, index, op.Size);
@ -1445,11 +1448,11 @@ namespace ARMeilleure.Instructions
{
Operand temp = context.Add(ne, me);
de = EmitSatQ(context, temp, op.Size, signedSrc: true, signedDst: signed);
de = EmitSignedSrcSatQ(context, temp, op.Size, signedDst: signed);
}
else /* if (op.Size == 3) */
{
de = EmitBinarySatQAccumulate(context, ne, me, signed);
de = signed ? EmitBinarySignedSatQAcc(context, ne, me) : EmitBinaryUnsignedSatQAcc(context, ne, me);
}
res = EmitVectorInsert(context, res, de, index, op.Size);
@ -1475,7 +1478,7 @@ namespace ARMeilleure.Instructions
me = EmitVectorExtract(context, ((OpCodeSimdReg)op).Rm, index, op.Size, signed);
}
Operand de = EmitSatQ(context, emit(ne, me), op.Size, true, signed);
Operand de = EmitSignedSrcSatQ(context, emit(ne, me), op.Size, signedDst: signed);
res = EmitVectorInsert(context, res, de, index, op.Size);
}
@ -1520,7 +1523,9 @@ namespace ARMeilleure.Instructions
{
Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size + 1, signedSrc);
Operand temp = EmitSatQ(context, ne, op.Size, signedSrc, signedDst);
Operand temp = signedSrc
? EmitSignedSrcSatQ(context, ne, op.Size, signedDst)
: EmitUnsignedSrcSatQ(context, ne, op.Size, signedDst);
res = EmitVectorInsert(context, res, temp, part + index, op.Size);
}
@ -1528,74 +1533,248 @@ namespace ARMeilleure.Instructions
context.Copy(d, res);
}
// TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned).
public static Operand EmitSatQ(ArmEmitterContext context, Operand op, int sizeDst, bool signedSrc, bool signedDst)
// 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)
{
if ((uint)sizeDst > 2u)
{
throw new ArgumentOutOfRangeException(nameof(sizeDst));
Debug.Assert(op.Type == OperandType.I64 && (uint)sizeDst <= 2u);
Operand lbl1 = Label();
Operand lblEnd = Label();
int eSize = 8 << sizeDst;
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), op);
context.BranchIf(lbl1, res, maxT, 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.Copy(res, minT);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
MethodInfo info;
// TSrc (16bit, 32bit, 64bit; unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned).
// 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);
if (signedSrc)
{
info = signedDst
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcSignedDstSatQ))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ));
}
else
{
info = signedDst
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ));
Operand lblEnd = Label();
int eSize = 8 << sizeDst;
Operand maxL = signedDst ? Const((1L << (eSize - 1)) - 1L) : Const((1UL << eSize) - 1UL);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op);
context.BranchIf(lblEnd, res, maxL, Comparison.LessOrEqualUI);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
return context.Call(info, op, Const(sizeDst));
// long UnarySignedSatQAbsOrNeg(long op);
private static Operand EmitUnarySignedSatQAbsOrNeg(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
Operand lblEnd = Label();
Operand minL = Const(long.MinValue);
Operand maxL = Const(long.MaxValue);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op);
context.BranchIf(lblEnd, res, minL, Comparison.NotEqual);
context.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// TSrc (64bit) == TDst (64bit); signed.
public static Operand EmitUnarySignedSatQAbsOrNeg(ArmEmitterContext context, Operand op)
// long BinarySignedSatQAdd(long op1, long op2);
private static Operand EmitBinarySignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnarySignedSatQAbsOrNeg)), op);
Operand lblEnd = Label();
Operand minL = Const(long.MinValue);
Operand maxL = Const(long.MaxValue);
Operand zero = Const(0L);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2));
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 isPositive = context.ICompareGreaterOrEqual(op1, zero);
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// TSrcs (64bit) == TDst (64bit); signed, unsigned.
public static Operand EmitBinarySatQAdd(ArmEmitterContext context, Operand op1, Operand op2, bool signed)
// ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2);
private static Operand EmitBinaryUnsignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
MethodInfo info = signed
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd));
Operand lblEnd = Label();
return context.Call(info, op1, op2);
Operand maxUL = Const(ulong.MaxValue);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2));
context.BranchIf(lblEnd, res, op1, Comparison.GreaterOrEqualUI);
context.Copy(res, maxUL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// TSrcs (64bit) == TDst (64bit); signed, unsigned.
public static Operand EmitBinarySatQSub(ArmEmitterContext context, Operand op1, Operand op2, bool signed)
// long BinarySignedSatQSub(long op1, long op2);
private static Operand EmitBinarySignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
MethodInfo info = signed
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub));
Operand lblEnd = Label();
return context.Call(info, op1, op2);
Operand minL = Const(long.MinValue);
Operand maxL = Const(long.MaxValue);
Operand zero = Const(0L);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Subtract(op1, op2));
Operand left = context.BitwiseExclusiveOr(op1, op2);
Operand right = context.BitwiseExclusiveOr(op1, res);
context.BranchIf(lblEnd, context.BitwiseAnd(left, right), zero, Comparison.GreaterOrEqual);
Operand isPositive = context.ICompareGreaterOrEqual(op1, zero);
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// TSrcs (64bit) == TDst (64bit); signed, unsigned.
public static Operand EmitBinarySatQAccumulate(ArmEmitterContext context, Operand op1, Operand op2, bool signed)
// ulong BinaryUnsignedSatQSub(ulong op1, ulong op2);
private static Operand EmitBinaryUnsignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
MethodInfo info = signed
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAcc));
Operand lblEnd = Label();
return context.Call(info, op1, op2);
Operand zero = Const(0L);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Subtract(op1, op2));
context.BranchIf(lblEnd, op1, op2, Comparison.GreaterOrEqualUI);
context.Copy(res, zero);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// long BinarySignedSatQAcc(ulong op1, long op2);
private static Operand EmitBinarySignedSatQAcc(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
Operand lbl1 = Label();
Operand lbl2 = Label();
Operand lblEnd = Label();
Operand maxL = Const(long.MaxValue);
Operand zero = Const(0L);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2));
context.BranchIf(lbl1, op1, maxL, Comparison.GreaterUI);
Operand notOp2AndRes = context.BitwiseAnd(context.BitwiseNot(op2), res);
context.BranchIf(lblEnd, notOp2AndRes, zero, 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.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.Copy(res, maxL);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
// ulong BinaryUnsignedSatQAcc(long op1, ulong op2);
private static Operand EmitBinaryUnsignedSatQAcc(ArmEmitterContext context, Operand op1, Operand op2)
{
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
Operand lbl1 = Label();
Operand lblEnd = Label();
Operand maxUL = Const(ulong.MaxValue);
Operand maxL = Const(long.MaxValue);
Operand zero = Const(0L);
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), context.Add(op1, op2));
context.BranchIf(lbl1, op1, zero, Comparison.Less);
context.BranchIf(lblEnd, res, 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.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);
return res;
}
public static Operand EmitFloatAbs(ArmEmitterContext context, Operand value, bool single, bool vector)

View File

@ -1004,7 +1004,7 @@ namespace ARMeilleure.Instructions
e = EmitShrImm64(context, e, signedSrc, roundConst, shift); // shift <= 32
}
e = EmitSatQ(context, e, op.Size, signedSrc, signedDst);
e = signedSrc ? EmitSignedSrcSatQ(context, e, op.Size, signedDst) : EmitUnsignedSrcSatQ(context, e, op.Size, signedDst);
res = EmitVectorInsert(context, res, e, part + index, op.Size);
}

View File

@ -91,7 +91,7 @@ namespace ARMeilleure.Instructions
}
else /* if (eSize != 64) */
{
return SignedSrcSignedDstSatQ(value << shiftLsB, size);
return SignedSrcSignedDstSatQ(value << shiftLsB, size); // InstEmitSimdHelper.EmitSignedSrcSatQ(signedDst: true).
}
}
else /* if (shiftLsB == 0) */
@ -135,7 +135,7 @@ namespace ARMeilleure.Instructions
}
else /* if (eSize != 64) */
{
return UnsignedSrcUnsignedDstSatQ(value << shiftLsB, size);
return UnsignedSrcUnsignedDstSatQ(value << shiftLsB, size); // InstEmitSimdHelper.EmitUnsignedSrcSatQ(signedDst: false).
}
}
else /* if (shiftLsB == 0) */
@ -509,7 +509,7 @@ namespace ARMeilleure.Instructions
#endregion
#region "Saturating"
public static long SignedSrcSignedDstSatQ(long op, int size)
private static long SignedSrcSignedDstSatQ(long op, int size)
{
ExecutionContext context = NativeInterface.GetContext();
@ -536,54 +536,7 @@ namespace ARMeilleure.Instructions
}
}
public static ulong SignedSrcUnsignedDstSatQ(long op, int size)
{
ExecutionContext context = NativeInterface.GetContext();
int eSize = 8 << size;
ulong tMaxValue = (1UL << eSize) - 1UL;
ulong tMinValue = 0UL;
if (op > (long)tMaxValue)
{
context.Fpsr |= FPSR.Qc;
return tMaxValue;
}
else if (op < (long)tMinValue)
{
context.Fpsr |= FPSR.Qc;
return tMinValue;
}
else
{
return (ulong)op;
}
}
public static long UnsignedSrcSignedDstSatQ(ulong op, int size)
{
ExecutionContext context = NativeInterface.GetContext();
int eSize = 8 << size;
long tMaxValue = (1L << (eSize - 1)) - 1L;
if (op > (ulong)tMaxValue)
{
context.Fpsr |= FPSR.Qc;
return tMaxValue;
}
else
{
return (long)op;
}
}
public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size)
private static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size)
{
ExecutionContext context = NativeInterface.GetContext();
@ -602,208 +555,6 @@ namespace ARMeilleure.Instructions
return op;
}
}
public static long UnarySignedSatQAbsOrNeg(long op)
{
ExecutionContext context = NativeInterface.GetContext();
if (op == long.MinValue)
{
context.Fpsr |= FPSR.Qc;
return long.MaxValue;
}
else
{
return op;
}
}
public static long BinarySignedSatQAdd(long op1, long op2)
{
ExecutionContext context = NativeInterface.GetContext();
long add = op1 + op2;
if ((~(op1 ^ op2) & (op1 ^ add)) < 0L)
{
context.Fpsr |= FPSR.Qc;
if (op1 < 0L)
{
return long.MinValue;
}
else
{
return long.MaxValue;
}
}
else
{
return add;
}
}
public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2)
{
ExecutionContext context = NativeInterface.GetContext();
ulong add = op1 + op2;
if ((add < op1) && (add < op2))
{
context.Fpsr |= FPSR.Qc;
return ulong.MaxValue;
}
else
{
return add;
}
}
public static long BinarySignedSatQSub(long op1, long op2)
{
ExecutionContext context = NativeInterface.GetContext();
long sub = op1 - op2;
if (((op1 ^ op2) & (op1 ^ sub)) < 0L)
{
context.Fpsr |= FPSR.Qc;
if (op1 < 0L)
{
return long.MinValue;
}
else
{
return long.MaxValue;
}
}
else
{
return sub;
}
}
public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2)
{
ExecutionContext context = NativeInterface.GetContext();
ulong sub = op1 - op2;
if (op1 < op2)
{
context.Fpsr |= FPSR.Qc;
return ulong.MinValue;
}
else
{
return sub;
}
}
public static long BinarySignedSatQAcc(ulong op1, long op2)
{
ExecutionContext context = NativeInterface.GetContext();
if (op1 <= (ulong)long.MaxValue)
{
// op1 from ulong.MinValue to (ulong)long.MaxValue
// op2 from long.MinValue to long.MaxValue
long add = (long)op1 + op2;
if ((~op2 & add) < 0L)
{
context.Fpsr |= FPSR.Qc;
return long.MaxValue;
}
else
{
return add;
}
}
else if (op2 >= 0L)
{
// op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
// op2 from (long)ulong.MinValue to long.MaxValue
context.Fpsr |= FPSR.Qc;
return long.MaxValue;
}
else
{
// op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
// op2 from long.MinValue to (long)ulong.MinValue - 1L
ulong add = op1 + (ulong)op2;
if (add > (ulong)long.MaxValue)
{
context.Fpsr |= FPSR.Qc;
return long.MaxValue;
}
else
{
return (long)add;
}
}
}
public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2)
{
ExecutionContext context = NativeInterface.GetContext();
if (op1 >= 0L)
{
// op1 from (long)ulong.MinValue to long.MaxValue
// op2 from ulong.MinValue to ulong.MaxValue
ulong add = (ulong)op1 + op2;
if ((add < (ulong)op1) && (add < op2))
{
context.Fpsr |= FPSR.Qc;
return ulong.MaxValue;
}
else
{
return add;
}
}
else if (op2 > (ulong)long.MaxValue)
{
// op1 from long.MinValue to (long)ulong.MinValue - 1L
// op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue
return (ulong)op1 + op2;
}
else
{
// op1 from long.MinValue to (long)ulong.MinValue - 1L
// op2 from ulong.MinValue to (ulong)long.MaxValue
long add = op1 + (long)op2;
if (add < (long)ulong.MinValue)
{
context.Fpsr |= FPSR.Qc;
return ulong.MinValue;
}
else
{
return (ulong)add;
}
}
}
#endregion
#region "Count"

View File

@ -127,7 +127,7 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
@ -140,12 +140,6 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAcc)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
@ -188,8 +182,6 @@ namespace ARMeilleure.Translation
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.SignedSrcSignedDstSatQ)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
@ -198,12 +190,9 @@ 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.UnarySignedSatQAbsOrNeg)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)));
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));

View File

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

View File

@ -44,15 +44,11 @@ namespace ARMeilleure.Translation
private readonly IJitMemoryAllocator _allocator;
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
private readonly ConcurrentDictionary<ulong, object> _backgroundSet;
private readonly ConcurrentStack<RejitRequest> _backgroundStack;
private readonly AutoResetEvent _backgroundTranslatorEvent;
private readonly ReaderWriterLock _backgroundTranslatorLock;
internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; }
internal TranslatorStubs Stubs { get; }
internal TranslatorQueue Queue { get; }
internal IMemoryManager Memory { get; }
private volatile int _threadCount;
@ -67,10 +63,7 @@ namespace ARMeilleure.Translation
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
_backgroundSet = new ConcurrentDictionary<ulong, object>();
_backgroundStack = new ConcurrentStack<RejitRequest>();
_backgroundTranslatorEvent = new AutoResetEvent(false);
_backgroundTranslatorLock = new ReaderWriterLock();
Queue = new TranslatorQueue();
JitCache.Initialize(allocator);
@ -87,43 +80,6 @@ namespace ARMeilleure.Translation
}
}
private void TranslateStackedSubs()
{
while (_threadCount != 0)
{
_backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite);
if (_backgroundStack.TryPop(out RejitRequest request) &&
_backgroundSet.TryRemove(request.Address, out _))
{
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) =>
{
EnqueueForDeletion(key, oldFunc);
return func;
});
if (PtcProfiler.Enabled)
{
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
}
RegisterFunction(request.Address, func);
_backgroundTranslatorLock.ReleaseReaderLock();
}
else
{
_backgroundTranslatorLock.ReleaseReaderLock();
_backgroundTranslatorEvent.WaitOne();
}
}
// Wake up any other background translator threads, to encourage them to exit.
_backgroundTranslatorEvent.Set();
}
public void Execute(State.ExecutionContext context, ulong address)
{
if (Interlocked.Increment(ref _threadCount) == 1)
@ -155,7 +111,7 @@ namespace ARMeilleure.Translation
{
bool last = i != 0 && i == unboundedThreadCount - 1;
Thread backgroundTranslatorThread = new Thread(TranslateStackedSubs)
Thread backgroundTranslatorThread = new Thread(BackgroundTranslate)
{
Name = "CPU.BackgroundTranslatorThread." + i,
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
@ -186,10 +142,9 @@ namespace ARMeilleure.Translation
if (Interlocked.Decrement(ref _threadCount) == 0)
{
_backgroundTranslatorEvent.Set();
ClearJitCache();
Queue.Dispose();
Stubs.Dispose();
FunctionTable.Dispose();
CountTable.Dispose();
@ -317,6 +272,27 @@ namespace ARMeilleure.Translation
return new TranslatedFunction(func, counter, funcSize, highCq);
}
private void BackgroundTranslate()
{
while (_threadCount != 0 && Queue.TryDequeue(out RejitRequest request))
{
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) =>
{
EnqueueForDeletion(key, oldFunc);
return func;
});
if (PtcProfiler.Enabled)
{
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
}
RegisterFunction(request.Address, func);
}
}
private struct Range
{
public ulong Start { get; }
@ -504,11 +480,7 @@ namespace ARMeilleure.Translation
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
{
if (_backgroundSet.TryAdd(guestAddress, null))
{
_backgroundStack.Push(new RejitRequest(guestAddress, mode));
_backgroundTranslatorEvent.Set();
}
Queue.Enqueue(guestAddress, mode);
}
private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func)
@ -542,26 +514,23 @@ namespace ARMeilleure.Translation
private void ClearRejitQueue(bool allowRequeue)
{
_backgroundTranslatorLock.AcquireWriterLock(Timeout.Infinite);
if (allowRequeue)
if (!allowRequeue)
{
while (_backgroundStack.TryPop(out var request))
Queue.Clear();
return;
}
lock (Queue.Sync)
{
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
{
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
{
Volatile.Write(ref func.CallCounter.Value, 0);
}
_backgroundSet.TryRemove(request.Address, out _);
}
}
else
{
_backgroundStack.Clear();
}
_backgroundTranslatorLock.ReleaseWriterLock();
}
}
}
}
}

View File

@ -0,0 +1,121 @@
using ARMeilleure.Diagnostics;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Threading;
namespace ARMeilleure.Translation
{
/// <summary>
/// Represents a queue of <see cref="RejitRequest"/>.
/// </summary>
/// <remarks>
/// This does not necessarily behave like a queue, i.e: a FIFO collection.
/// </remarks>
sealed class TranslatorQueue : IDisposable
{
private bool _disposed;
private readonly Stack<RejitRequest> _requests;
private readonly HashSet<ulong> _requestAddresses;
/// <summary>
/// Gets the object used to synchronize access to the <see cref="TranslatorQueue"/>.
/// </summary>
public object Sync { get; }
/// <summary>
/// Gets the number of requests in the <see cref="TranslatorQueue"/>.
/// </summary>
public int Count => _requests.Count;
/// <summary>
/// Initializes a new instance of the <see cref="TranslatorQueue"/> class.
/// </summary>
public TranslatorQueue()
{
Sync = new object();
_requests = new Stack<RejitRequest>();
_requestAddresses = new HashSet<ulong>();
}
/// <summary>
/// Enqueues a request with the specified <paramref name="address"/> and <paramref name="mode"/>.
/// </summary>
/// <param name="address">Address of request</param>
/// <param name="mode"><see cref="ExecutionMode"/> of request</param>
public void Enqueue(ulong address, ExecutionMode mode)
{
lock (Sync)
{
if (_requestAddresses.Add(address))
{
_requests.Push(new RejitRequest(address, mode));
TranslatorEventSource.Log.RejitQueueAdd(1);
Monitor.Pulse(Sync);
}
}
}
/// <summary>
/// Tries to dequeue a <see cref="RejitRequest"/>. This will block the thread until a <see cref="RejitRequest"/>
/// is enqueued or the <see cref="TranslatorQueue"/> is disposed.
/// </summary>
/// <param name="result"><see cref="RejitRequest"/> dequeued</param>
/// <returns><see langword="true"/> on success; otherwise <see langword="false"/></returns>
public bool TryDequeue(out RejitRequest result)
{
while (!_disposed)
{
lock (Sync)
{
if (_requests.TryPop(out result))
{
_requestAddresses.Remove(result.Address);
TranslatorEventSource.Log.RejitQueueAdd(-1);
return true;
}
Monitor.Wait(Sync);
}
}
result = default;
return false;
}
/// <summary>
/// Clears the <see cref="TranslatorQueue"/>.
/// </summary>
public void Clear()
{
lock (Sync)
{
TranslatorEventSource.Log.RejitQueueAdd(-_requests.Count);
_requests.Clear();
_requestAddresses.Clear();
Monitor.PulseAll(Sync);
}
}
/// <summary>
/// Releases all resources used by the <see cref="TranslatorQueue"/> instance.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
Clear();
}
}
}
}

View File

@ -151,6 +151,190 @@ namespace Ryujinx.Graphics.GAL
public static class FormatExtensions
{
/// <summary>
/// The largest scalar size for a buffer format.
/// </summary>
public const int MaxBufferFormatScalarSize = 4;
/// <summary>
/// Gets the byte size for a single component of this format, or its packed size.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>Byte size for a single component, or packed size</returns>
public static int GetScalarSize(this Format format)
{
switch (format)
{
case Format.R8Unorm:
case Format.R8Snorm:
case Format.R8Uint:
case Format.R8Sint:
case Format.R8G8Unorm:
case Format.R8G8Snorm:
case Format.R8G8Uint:
case Format.R8G8Sint:
case Format.R8G8B8Unorm:
case Format.R8G8B8Snorm:
case Format.R8G8B8Uint:
case Format.R8G8B8Sint:
case Format.R8G8B8A8Unorm:
case Format.R8G8B8A8Snorm:
case Format.R8G8B8A8Uint:
case Format.R8G8B8A8Sint:
case Format.R8G8B8A8Srgb:
case Format.R4G4Unorm:
case Format.R8Uscaled:
case Format.R8Sscaled:
case Format.R8G8Uscaled:
case Format.R8G8Sscaled:
case Format.R8G8B8Uscaled:
case Format.R8G8B8Sscaled:
case Format.R8G8B8A8Uscaled:
case Format.R8G8B8A8Sscaled:
case Format.B8G8R8A8Unorm:
case Format.B8G8R8A8Srgb:
return 1;
case Format.R16Float:
case Format.R16Unorm:
case Format.R16Snorm:
case Format.R16Uint:
case Format.R16Sint:
case Format.R16G16Float:
case Format.R16G16Unorm:
case Format.R16G16Snorm:
case Format.R16G16Uint:
case Format.R16G16Sint:
case Format.R16G16B16Float:
case Format.R16G16B16Unorm:
case Format.R16G16B16Snorm:
case Format.R16G16B16Uint:
case Format.R16G16B16Sint:
case Format.R16G16B16A16Float:
case Format.R16G16B16A16Unorm:
case Format.R16G16B16A16Snorm:
case Format.R16G16B16A16Uint:
case Format.R16G16B16A16Sint:
case Format.R4G4B4A4Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
case Format.R5G6B5Unorm:
case Format.R16Uscaled:
case Format.R16Sscaled:
case Format.R16G16Uscaled:
case Format.R16G16Sscaled:
case Format.R16G16B16Uscaled:
case Format.R16G16B16Sscaled:
case Format.R16G16B16A16Uscaled:
case Format.R16G16B16A16Sscaled:
case Format.B5G6R5Unorm:
case Format.B5G5R5A1Unorm:
case Format.A1B5G5R5Unorm:
return 2;
case Format.R32Float:
case Format.R32Uint:
case Format.R32Sint:
case Format.R32G32Float:
case Format.R32G32Uint:
case Format.R32G32Sint:
case Format.R32G32B32Float:
case Format.R32G32B32Uint:
case Format.R32G32B32Sint:
case Format.R32G32B32A32Float:
case Format.R32G32B32A32Uint:
case Format.R32G32B32A32Sint:
case Format.R10G10B10A2Unorm:
case Format.R10G10B10A2Uint:
case Format.R11G11B10Float:
case Format.R9G9B9E5Float:
case Format.R32Uscaled:
case Format.R32Sscaled:
case Format.R32G32Uscaled:
case Format.R32G32Sscaled:
case Format.R32G32B32Uscaled:
case Format.R32G32B32Sscaled:
case Format.R32G32B32A32Uscaled:
case Format.R32G32B32A32Sscaled:
case Format.R10G10B10A2Snorm:
case Format.R10G10B10A2Sint:
case Format.R10G10B10A2Uscaled:
case Format.R10G10B10A2Sscaled:
return 4;
case Format.S8Uint:
return 1;
case Format.D16Unorm:
return 2;
case Format.S8UintD24Unorm:
case Format.D32Float:
case Format.D24UnormS8Uint:
return 4;
case Format.D32FloatS8Uint:
return 8;
case Format.Bc1RgbaUnorm:
case Format.Bc1RgbaSrgb:
return 8;
case Format.Bc2Unorm:
case Format.Bc3Unorm:
case Format.Bc2Srgb:
case Format.Bc3Srgb:
case Format.Bc4Unorm:
case Format.Bc4Snorm:
case Format.Bc5Unorm:
case Format.Bc5Snorm:
case Format.Bc7Unorm:
case Format.Bc7Srgb:
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
return 16;
case Format.Etc2RgbUnorm:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbSrgb:
case Format.Etc2RgbPtaSrgb:
return 8;
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbaSrgb:
return 16;
case Format.Astc4x4Unorm:
case Format.Astc5x4Unorm:
case Format.Astc5x5Unorm:
case Format.Astc6x5Unorm:
case Format.Astc6x6Unorm:
case Format.Astc8x5Unorm:
case Format.Astc8x6Unorm:
case Format.Astc8x8Unorm:
case Format.Astc10x5Unorm:
case Format.Astc10x6Unorm:
case Format.Astc10x8Unorm:
case Format.Astc10x10Unorm:
case Format.Astc12x10Unorm:
case Format.Astc12x12Unorm:
case Format.Astc4x4Srgb:
case Format.Astc5x4Srgb:
case Format.Astc5x5Srgb:
case Format.Astc6x5Srgb:
case Format.Astc6x6Srgb:
case Format.Astc8x5Srgb:
case Format.Astc8x6Srgb:
case Format.Astc8x8Srgb:
case Format.Astc10x5Srgb:
case Format.Astc10x6Srgb:
case Format.Astc10x8Srgb:
case Format.Astc10x10Srgb:
case Format.Astc12x10Srgb:
case Format.Astc12x12Srgb:
return 16;
}
return 1;
}
/// <summary>
/// Checks if the texture format is valid to use as image format.
/// </summary>

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3525;
private const uint CodeGenVersion = 3672;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@ -153,6 +153,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
break;
case Instruction.LoadConstant:
operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value));
break;
case Instruction.Maximum:
EvaluateBinary(operation, (x, y) => Math.Max(x, y));
break;

View File

@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<MemoryAllocation> _allocationAuto;
private readonly ulong _bufferHandle;
private CacheByRange<BufferHolder> _cachedConvertedIndexBuffers;
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
public int Size { get; }
@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (isWrite)
{
_cachedConvertedIndexBuffers.Clear();
_cachedConvertedBuffers.Clear();
}
return _buffer;
@ -364,13 +364,35 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
if (!_cachedConvertedIndexBuffers.TryGetValue(offset, size, out var holder))
var key = new I8ToI16CacheKey();
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
_cachedConvertedIndexBuffers.Add(offset, size, holder);
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
{
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
int alignedStride = (stride + (alignment - 1)) & -alignment;
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
@ -382,7 +404,7 @@ namespace Ryujinx.Graphics.Vulkan
_buffer.Dispose();
_allocationAuto.Dispose();
_cachedConvertedIndexBuffers.Dispose();
_cachedConvertedBuffers.Dispose();
}
}
}

View File

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

View File

@ -7,28 +7,28 @@ namespace Ryujinx.Graphics.Vulkan
{
public static BufferState Null => new BufferState(null, 0, 0);
private readonly Auto<DisposableBuffer> _buffer;
private readonly int _offset;
private readonly int _size;
private readonly ulong _stride;
private readonly IndexType _type;
private readonly Auto<DisposableBuffer> _buffer;
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
{
_buffer = buffer;
_offset = offset;
_size = size;
_stride = 0;
_type = type;
buffer?.IncrementReferenceCount();
}
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, ulong stride = 0UL)
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{
_buffer = buffer;
_offset = offset;
_size = size;
_stride = stride;
_type = IndexType.Uint16;
buffer?.IncrementReferenceCount();
}
@ -51,30 +51,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
{
if (_buffer != null)
{
var buffer = _buffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
}
}
public void Dispose()
{
_buffer?.DecrementReferenceCount();

View File

@ -3,29 +3,110 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{
struct CacheByRange<T> where T : IDisposable
interface ICacheKey : IDisposable
{
private Dictionary<ulong, T> _ranges;
public void Add(int offset, int size, T value)
{
EnsureInitialized();
_ranges.Add(PackRange(offset, size), value);
bool KeyEqual(ICacheKey other);
}
public bool TryGetValue(int offset, int size, out T value)
struct I8ToI16CacheKey : ICacheKey
{
EnsureInitialized();
return _ranges.TryGetValue(PackRange(offset, size), out value);
public I8ToI16CacheKey() { }
public bool KeyEqual(ICacheKey other)
{
return other is I8ToI16CacheKey;
}
public void Dispose() { }
}
struct AlignedVertexBufferCacheKey : ICacheKey
{
private readonly int _stride;
private readonly int _alignment;
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public AlignedVertexBufferCacheKey(VulkanRenderer gd, int stride, int alignment)
{
_gd = gd;
_stride = stride;
_alignment = alignment;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is AlignedVertexBufferCacheKey entry &&
entry._stride == _stride &&
entry._alignment == _alignment;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyVertexBuffer(_buffer);
}
}
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
{
public ICacheKey Key;
public T Value;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
}
}
private Dictionary<ulong, List<Entry>> _ranges;
public void Add(int offset, int size, ICacheKey key, T value)
{
List<Entry> entries = GetEntries(offset, size);
entries.Add(new Entry(key, value));
}
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List<Entry> entries = GetEntries(offset, size);
foreach (Entry entry in entries)
{
if (entry.Key.KeyEqual(key))
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
public void Clear()
{
if (_ranges != null)
{
foreach (T value in _ranges.Values)
foreach (List<Entry> entries in _ranges.Values)
{
value.Dispose();
foreach (Entry entry in entries)
{
entry.Key.Dispose();
entry.Value.Dispose();
}
}
_ranges.Clear();
@ -33,12 +114,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void EnsureInitialized()
private List<Entry> GetEntries(int offset, int size)
{
if (_ranges == null)
{
_ranges = new Dictionary<ulong, T>();
_ranges = new Dictionary<ulong, List<Entry>>();
}
ulong key = PackRange(offset, size);
List<Entry> value;
if (!_ranges.TryGetValue(key, out value))
{
value = new List<Entry>();
_ranges.Add(key, value);
}
return value;
}
private static ulong PackRange(int offset, int size)

View File

@ -185,6 +185,34 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Storage);
}
public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
{
for (int i = 0; i < buffers.Length; i++)
{
var vkBuffer = buffers[i];
int index = first + i;
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
DescriptorBufferInfo info = new DescriptorBufferInfo()
{
Offset = 0,
Range = Vk.WholeSize
};
ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index];
if (vkBuffer != currentVkBuffer || currentInfo.Offset != info.Offset || currentInfo.Range != info.Range)
{
_storageSet[index] = false;
currentInfo = info;
currentVkBuffer = vkBuffer;
}
}
SignalDirty(DirtyFlags.Storage);
}
public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
if (texture == null)
@ -388,8 +416,15 @@ namespace Ryujinx.Graphics.Vulkan
}
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
if (program.HasMinimalLayout)
{
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
}
else
{
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
}
}
else if (setIndex == PipelineBase.TextureSetIndex)
{
if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)

View File

@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsSubgroupSizeControl;
public readonly bool SupportsShaderInt8;
public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsMultiView;
@ -29,6 +30,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsFragmentShaderInterlock,
bool supportsGeometryShaderPassthrough,
bool supportsSubgroupSizeControl,
bool supportsShaderInt8,
bool supportsConditionalRendering,
bool supportsExtendedDynamicState,
bool supportsMultiView,
@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
SupportsShaderInt8 = supportsShaderInt8;
SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsMultiView = supportsMultiView;

View File

@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorBlit;
private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange;
public HelperShader(VulkanRenderer gd, Device device)
{
@ -39,14 +40,14 @@ namespace Ryujinx.Graphics.Vulkan
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var fragmentBindings2 = new ShaderBindings(
@ -57,8 +58,19 @@ namespace Ryujinx.Graphics.Vulkan
_programColorClear = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var strideChangeBindings = new ShaderBindings(
new[] { 0 },
new[] { 1, 2 },
Array.Empty<int>(),
Array.Empty<int>());
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
}
@ -163,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish();
_pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
}
@ -291,45 +303,100 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
{
// TODO: Do this with a compute shader?
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, size).Value;
var dstBuffer = dst.GetBuffer().Get(cbs, 0, size * 2).Value;
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
var bufferCopy = new BufferCopy[size];
for (ulong i = 0; i < (ulong)size; i++)
{
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i, i * 2, 1);
ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2);
}
public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride)
{
bool supportsUint8 = gd.Capabilities.SupportsShaderInt8;
int elems = size / stride;
int newSize = elems * newStride;
var srcBufferAuto = src.GetBuffer();
var dstBufferAuto = dst.GetBuffer();
var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
var access = supportsUint8 ? AccessFlags.AccessShaderWriteBit : AccessFlags.AccessTransferWriteBit;
var stage = supportsUint8 ? PipelineStageFlags.PipelineStageComputeShaderBit : PipelineStageFlags.PipelineStageTransferBit;
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.AccessTransferWriteBit,
access,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageTransferBit,
stage,
0,
size * 2);
newSize);
if (supportsUint8)
{
const int ParamsBufferSize = 16;
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
shaderParams[0] = stride;
shaderParams[1] = newStride;
shaderParams[2] = size;
shaderParams[3] = srcOffset;
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
_pipeline.SetCommandBuffer(cbs);
Span<BufferRange> cbRanges = stackalloc BufferRange[1];
cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize);
_pipeline.SetUniformBuffers(0, cbRanges);
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
sbRanges[0] = srcBufferAuto;
sbRanges[1] = dstBufferAuto;
_pipeline.SetStorageBuffers(1, sbRanges);
_pipeline.SetProgram(_programStrideChange);
_pipeline.DispatchCompute(1, 1, 1);
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs);
}
else
{
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
var bufferCopy = new BufferCopy[elems];
for (ulong i = 0; i < (ulong)elems; i++)
{
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride);
}
fixed (BufferCopy* pBufferCopy = bufferCopy)
{
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)size, pBufferCopy);
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy);
}
}
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
AccessFlags.AccessTransferWriteBit,
access,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.PipelineStageTransferBit,
stage,
PipelineStageFlags.PipelineStageAllCommandsBit,
0,
size * 2);
newSize);
}
protected virtual void Dispose(bool disposing)

View File

@ -2,6 +2,7 @@
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Vulkan
{
@ -50,14 +51,14 @@ namespace Ryujinx.Graphics.Vulkan
private BufferState _indexBuffer;
private readonly BufferState[] _transformFeedbackBuffers;
private readonly BufferState[] _vertexBuffers;
private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor;
public SupportBufferUpdater SupportBufferUpdater;
private bool _needsIndexBufferRebind;
private bool _needsTransformFeedbackBuffersRebind;
private bool _needsVertexBuffersRebind;
private bool _tfEnabled;
private bool _tfActive;
@ -79,14 +80,14 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater = new DescriptorSetUpdater(gd, this);
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
_vertexBuffers = new BufferState[Constants.MaxVertexBuffers + 1];
_vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1];
const int EmptyVbSize = 16;
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
emptyVb.SetData(0, new byte[EmptyVbSize]);
_vertexBuffers[0] = new BufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0UL);
_needsVertexBuffersRebind = true;
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0);
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
@ -229,6 +230,17 @@ namespace Ryujinx.Graphics.Vulkan
BufferHolder.Copy(Gd, Cbs, src, dst, srcOffset, dstOffset, size);
}
public void DirtyVertexBuffer(Auto<DisposableBuffer> buffer)
{
for (int i = 0; i < _vertexBuffers.Length; i++)
{
if (_vertexBuffers[i].BoundEquals(buffer))
{
_vertexBuffersDirty |= 1UL << i;
}
}
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
if (!_program.IsLinked)
@ -345,6 +357,11 @@ namespace Ryujinx.Graphics.Vulkan
_tfEnabled = false;
}
public bool IsCommandBufferActive(CommandBuffer cb)
{
return CommandBuffer.Handle == cb.Handle;
}
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!Gd.Capabilities.SupportsIndirectParameters)
@ -689,6 +706,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
}
public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
{
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
}
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
_descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler);
@ -732,12 +754,22 @@ namespace Ryujinx.Graphics.Vulkan
{
var formatCapabilities = Gd.FormatCapabilities;
Span<int> newVbScalarSizes = stackalloc int[Constants.MaxVertexBuffers];
int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length);
uint dirtyVbSizes = 0;
for (int i = 0; i < count; i++)
{
var attribute = vertexAttribs[i];
var bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1;
var rawIndex = attribute.BufferIndex;
var bufferIndex = attribute.IsZero ? 0 : rawIndex + 1;
if (!attribute.IsZero)
{
newVbScalarSizes[rawIndex] = Math.Max(newVbScalarSizes[rawIndex], attribute.Format.GetScalarSize());
dirtyVbSizes |= 1u << rawIndex;
}
_newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription(
(uint)i,
@ -746,6 +778,21 @@ namespace Ryujinx.Graphics.Vulkan
(uint)attribute.Offset);
}
while (dirtyVbSizes != 0)
{
int dirtyBit = BitOperations.TrailingZeroCount(dirtyVbSizes);
ref var buffer = ref _vertexBuffers[dirtyBit + 1];
if (buffer.AttributeScalarAlignment != newVbScalarSizes[dirtyBit])
{
_vertexBuffersDirty |= 1UL << (dirtyBit + 1);
buffer.AttributeScalarAlignment = newVbScalarSizes[dirtyBit];
}
dirtyVbSizes &= ~(1u << dirtyBit);
}
_newState.VertexAttributeDescriptionsCount = (uint)count;
SignalStateChange();
}
@ -792,14 +839,37 @@ namespace Ryujinx.Graphics.Vulkan
}
}
_vertexBuffers[binding].Dispose();
_vertexBuffers[binding] = new BufferState(
ref var buffer = ref _vertexBuffers[binding];
int oldScalarAlign = buffer.AttributeScalarAlignment;
buffer.Dispose();
if ((vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{
buffer = new VertexBufferState(
vb,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
(ulong)vertexBuffer.Stride);
vertexBuffer.Stride);
_vertexBuffers[binding].BindVertexBuffer(Gd, Cbs, (uint)binding);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState);
}
else
{
// May need to be rewritten. Bind this buffer before draw.
buffer = new VertexBufferState(
vertexBuffer.Buffer.Handle,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
_vertexBuffersDirty |= 1UL << binding;
}
buffer.AttributeScalarAlignment = oldScalarAlign;
}
}
}
@ -907,7 +977,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_needsIndexBufferRebind = true;
_needsTransformFeedbackBuffersRebind = true;
_needsVertexBuffersRebind = true;
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange();
_dynamicState.ForceAllDirty();
@ -1053,13 +1123,6 @@ namespace Ryujinx.Graphics.Vulkan
// Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit();
if (_stateDirty || Pbp != pbp)
{
CreatePipeline(pbp);
_stateDirty = false;
Pbp = pbp;
}
if (_needsIndexBufferRebind)
{
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
@ -1078,14 +1141,23 @@ namespace Ryujinx.Graphics.Vulkan
_needsTransformFeedbackBuffersRebind = false;
}
if (_needsVertexBuffersRebind)
if (_vertexBuffersDirty != 0)
{
for (int i = 0; i < Constants.MaxVertexBuffers + 1; i++)
while (_vertexBuffersDirty != 0)
{
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i);
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
_vertexBuffersDirty &= ~(1u << i);
}
}
_needsVertexBuffersRebind = false;
if (_stateDirty || Pbp != pbp)
{
CreatePipeline(pbp);
_stateDirty = false;
Pbp = pbp;
}
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, pbp);

View File

@ -202,6 +202,9 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.Topology = state.Topology.Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
Span<int> vbScalarSizes = stackalloc int[vbCount];
for (int i = 0; i < vaCount; i++)
{
@ -213,13 +216,16 @@ namespace Ryujinx.Graphics.Vulkan
(uint)bufferIndex,
gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format),
(uint)attribute.Offset);
if (!attribute.IsZero && bufferIndex < vbCount)
{
vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.GetScalarSize(), vbScalarSizes[bufferIndex - 1]);
}
}
int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
for (int i = 0; i < vbCount; i++)
{
var vertexBuffer = state.VertexBuffers[i];
@ -228,10 +234,17 @@ namespace Ryujinx.Graphics.Vulkan
{
var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex;
int alignedStride = vertexBuffer.Stride;
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
{
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment;
}
// TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1,
(uint)vertexBuffer.Stride,
(uint)alignedStride,
inputRate);
}
}

View File

@ -199,6 +199,16 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void Restore()
{
if (Pipeline != null)
{
Gd.Api.CmdBindPipeline(CommandBuffer, Pbp, Pipeline.Get(Cbs).Value);
}
SignalCommandBufferChange();
}
public void FlushCommandsImpl()
{
EndRenderPass();
@ -220,18 +230,13 @@ namespace Ryujinx.Graphics.Vulkan
// Restore per-command buffer state.
if (Pipeline != null)
{
Gd.Api.CmdBindPipeline(CommandBuffer, Pbp, Pipeline.Get(Cbs).Value);
}
foreach (var queryPool in _activeQueries)
{
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, 0);
}
SignalCommandBufferChange();
Restore();
}
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset)

View File

@ -40,5 +40,15 @@ namespace Ryujinx.Graphics.Vulkan
{
EndRenderPass();
}
public void Finish(VulkanRenderer gd, CommandBufferScoped cbs)
{
Finish();
if (gd.PipelineInternal.IsCommandBufferActive(cbs.CommandBuffer))
{
gd.PipelineInternal.Restore();
}
}
}
}

View File

@ -142,18 +142,20 @@ namespace Ryujinx.Graphics.Vulkan
int stagesCount = shaders.Length;
int uCount = 0;
int sCount = 0;
int tCount = 0;
int iCount = 0;
foreach (var shader in shaders)
{
uCount += shader.Bindings.UniformBufferBindings.Count;
sCount += shader.Bindings.StorageBufferBindings.Count;
tCount += shader.Bindings.TextureBindings.Count;
iCount += shader.Bindings.ImageBindings.Count;
}
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
@ -180,22 +182,11 @@ namespace Ryujinx.Graphics.Vulkan
}
}
void SetStorage(DescriptorSetLayoutBinding* bindings, ref int start, int count)
{
bindings[start++] = new DescriptorSetLayoutBinding
{
Binding = (uint)start,
DescriptorType = DescriptorType.StorageBuffer,
DescriptorCount = (uint)count,
StageFlags = stageFlags
};
}
// TODO: Support buffer textures and images here.
// This is only used for the helper shaders on the backend, and we don't use buffer textures on them
// so far, so it's not really necessary right now.
Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
SetStorage(sLayoutBindings, ref sIndex, shader.Bindings.StorageBufferBindings.Count);
Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
}
@ -213,7 +204,7 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.DescriptorSetLayoutCreateInfo,
PBindings = sLayoutBindings,
BindingCount = (uint)stagesCount
BindingCount = (uint)sCount
};
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()

View File

@ -0,0 +1,64 @@
#version 450 core
#extension GL_EXT_shader_8bit_storage : require
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout (std140, set = 0, binding = 0) uniform stride_arguments
{
ivec4 stride_arguments_data;
};
layout (std430, set = 1, binding = 1) buffer in_s
{
uint8_t[] in_data;
};
layout (std430, set = 1, binding = 2) buffer out_s
{
uint8_t[] out_data;
};
void main()
{
// Determine what slice of the stride copies this invocation will perform.
int sourceStride = stride_arguments_data.x;
int targetStride = stride_arguments_data.y;
int bufferSize = stride_arguments_data.z;
int sourceOffset = stride_arguments_data.w;
int strideRemainder = targetStride - sourceStride;
int invocations = int(gl_WorkGroupSize.x);
int copiesRequired = bufferSize / sourceStride;
// Find the copies that this invocation should perform.
// - Copies that all invocations perform.
int allInvocationCopies = copiesRequired / invocations;
// - Extra remainder copy that this invocation performs.
int index = int(gl_LocalInvocationID.x);
int extra = (index < (copiesRequired % invocations)) ? 1 : 0;
int copyCount = allInvocationCopies + extra;
// Finally, get the starting offset. Make sure to count extra copies.
int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index);
int srcOffset = sourceOffset + startCopy * sourceStride;
int dstOffset = startCopy * targetStride;
// Perform the copies for this region
for (int i=0; i<copyCount; i++) {
for (int j=0; j<sourceStride; j++) {
out_data[dstOffset++] = in_data[srcOffset++];
}
for (int j=0; j<strideRemainder; j++) {
out_data[dstOffset++] = uint8_t(0);
}
}
}

View File

@ -2,6 +2,249 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
{
static class ShaderBinaries
{
public static readonly byte[] ChangeBufferStrideShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x8E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x60, 0x11, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00,
0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65,
0x72, 0x5F, 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00,
0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70,
0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x64, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47,
0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61,
0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61,
0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x13, 0x00, 0x00, 0x00, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x62, 0x75, 0x66, 0x66,
0x65, 0x72, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x52, 0x65,
0x6D, 0x61, 0x69, 0x6E, 0x64, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00,
0x69, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00,
0x25, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x64, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x29, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x6C, 0x49,
0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00,
0x05, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00,
0x05, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x4C, 0x6F, 0x63, 0x61, 0x6C,
0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x79, 0x43, 0x6F, 0x75, 0x6E,
0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x42, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x72,
0x74, 0x43, 0x6F, 0x70, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x52, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x70, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00,
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00,
0x05, 0x00, 0x03, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x7B, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8D, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x03, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x06, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00,
0x55, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00,
0xF6, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x58, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00,
0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x67, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x61, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x4C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x68, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x75, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x62, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00,
0x7E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x80, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x84, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x52, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x71, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00,
0x86, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00,
0x87, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x5A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x59, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
public static readonly byte[] ColorBlitClearAlphaFragmentShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00,

View File

@ -0,0 +1,131 @@
using BufferHandle = Ryujinx.Graphics.GAL.BufferHandle;
namespace Ryujinx.Graphics.Vulkan
{
internal struct VertexBufferState
{
public static VertexBufferState Null => new VertexBufferState(null, 0, 0, 0);
private readonly int _offset;
private readonly int _size;
private readonly int _stride;
private readonly BufferHandle _handle;
private Auto<DisposableBuffer> _buffer;
internal readonly int DescriptorIndex;
internal int AttributeScalarAlignment;
public VertexBufferState(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0)
{
_buffer = buffer;
_handle = BufferHandle.Null;
_offset = offset;
_size = size;
_stride = stride;
DescriptorIndex = descriptorIndex;
AttributeScalarAlignment = 1;
buffer?.IncrementReferenceCount();
}
public VertexBufferState(BufferHandle handle, int descriptorIndex, int offset, int size, int stride = 0)
{
// This buffer state may be rewritten at bind time, so it must be retrieved on bind.
_buffer = null;
_handle = handle;
_offset = offset;
_size = size;
_stride = stride;
DescriptorIndex = descriptorIndex;
AttributeScalarAlignment = 1;
}
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state)
{
var autoBuffer = _buffer;
if (_handle != BufferHandle.Null)
{
// May need to restride the vertex buffer.
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
{
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
int stride = (_stride + (alignment - 1)) & -alignment;
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)(_size / _stride) * (ulong)stride,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
_buffer = autoBuffer;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
return;
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
}
}
if (autoBuffer != null)
{
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
(ulong)_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
}
}
public bool BoundEquals(Auto<DisposableBuffer> buffer)
{
return _buffer == buffer;
}
public void Dispose()
{
// Only dispose if this buffer is not refetched on each bind.
if (_handle == BufferHandle.Null)
{
_buffer?.DecrementReferenceCount();
}
}
}
}

View File

@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
"VK_EXT_fragment_shader_interlock",
"VK_EXT_index_type_uint8",
"VK_EXT_robustness2",
"VK_KHR_shader_float16_int8",
"VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough"

View File

@ -188,11 +188,22 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
};
PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new PhysicalDeviceShaderFloat16Int8FeaturesKHR()
{
SType = StructureType.PhysicalDeviceShaderFloat16Int8Features
};
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
features2.PNext = &featuresRobustness2;
}
if (supportedExtensions.Contains("VK_KHR_shader_float16_int8"))
{
featuresShaderInt8.PNext = features2.PNext;
features2.PNext = &featuresShaderInt8;
}
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
Capabilities = new HardwareCapabilities(
@ -202,6 +213,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
supportedExtensions.Contains("VK_EXT_subgroup_size_control"),
featuresShaderInt8.ShaderInt8,
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport,
@ -506,6 +518,24 @@ namespace Ryujinx.Graphics.Vulkan
PrintGpuInformation();
}
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{
if (Vendor != Vendor.Nvidia)
{
// Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid.
// Guest applications do this, NVIDIA GPUs are OK with it, others are not.
alignment = attrScalarAlignment;
return true;
}
alignment = 1;
return false;
}
public void PreFrame()
{
_syncManager.Cleanup();

View File

@ -336,6 +336,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
context.Memory.Write(outputBufferPosition + (ulong)(i * Unsafe.SizeOf<PollEventData>()), events[i].Data);
}
// In case of non blocking call timeout should not be returned.
if (timeout == 0 && errno == LinuxError.ETIMEDOUT)
{
errno = LinuxError.SUCCESS;
}
return WriteBsdResult(context, updateCount, errno);
}
@ -567,8 +573,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
if (socket != null)
{
errno = LinuxError.ENOTCONN;
if (socket.RemoteEndPoint != null)
{
errno = LinuxError.SUCCESS;
@ -576,6 +585,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
WriteBsdResult(context, 0, errno);
context.ResponseData.Write(Unsafe.SizeOf<BsdSockAddr>());
}
}
return WriteBsdResult(context, 0, errno);
}
@ -876,6 +886,91 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return WriteBsdResult(context, newSockFd, errno);
}
[CommandHipc(29)] // 7.0.0+
// RecvMMsg(u32 fd, u32 vlen, u32 flags, u32 reserved, nn::socket::TimeVal timeout) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
public ResultCode RecvMMsg(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
int vlen = context.RequestData.ReadInt32();
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
uint reserved = context.RequestData.ReadUInt32();
TimeVal timeout = context.RequestData.ReadStruct<TimeVal>();
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
int result = -1;
if (socket != null)
{
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
if (errno == LinuxError.SUCCESS)
{
errno = socket.RecvMMsg(out result, message, socketFlags, timeout);
if (errno == LinuxError.SUCCESS)
{
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
}
}
}
if (errno == LinuxError.SUCCESS)
{
SetResultErrno(socket, result);
receiveRegion.Dispose();
}
return WriteBsdResult(context, result, errno);
}
[CommandHipc(30)] // 7.0.0+
// SendMMsg(u32 fd, u32 vlen, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
public ResultCode SendMMsg(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
int vlen = context.RequestData.ReadInt32();
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
int result = -1;
if (socket != null)
{
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
if (errno == LinuxError.SUCCESS)
{
errno = socket.SendMMsg(out result, message, socketFlags);
if (errno == LinuxError.SUCCESS)
{
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
}
}
}
if (errno == LinuxError.SUCCESS)
{
SetResultErrno(socket, result);
receiveRegion.Dispose();
}
return WriteBsdResult(context, result, errno);
}
[CommandHipc(31)] // 7.0.0+
// EventFd(u64 initval, nn::socket::EventFdFlags flags) -> (i32 ret, u32 bsd_errno)
public ResultCode EventFd(ServiceCtx context)

View File

@ -25,7 +25,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
LinuxError SendTo(out int sendSize, ReadOnlySpan<byte> buffer, int size, BsdSocketFlags flags, IPEndPoint remoteEndPoint);
LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout);
LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags);
LinuxError GetSocketOption(BsdSocketOption option, SocketOptionLevel level, Span<byte> optionValue);
LinuxError SetSocketOption(BsdSocketOption option, SocketOptionLevel level, ReadOnlySpan<byte> optionValue);
bool Poll(int microSeconds, SelectMode mode);

View File

@ -1,5 +1,7 @@
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
@ -356,5 +358,165 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
return Send(out writeSize, buffer, BsdSocketFlags.None);
}
private bool CanSupportMMsgHdr(BsdMMsgHdr message)
{
for (int i = 0; i < message.Messages.Length; i++)
{
if (message.Messages[i].Name != null ||
message.Messages[i].Control != null)
{
return false;
}
}
return true;
}
private static IList<ArraySegment<byte>> ConvertMessagesToBuffer(BsdMMsgHdr message)
{
int segmentCount = 0;
int index = 0;
foreach (BsdMsgHdr msgHeader in message.Messages)
{
segmentCount += msgHeader.Iov.Length;
}
ArraySegment<byte>[] buffers = new ArraySegment<byte>[segmentCount];
foreach (BsdMsgHdr msgHeader in message.Messages)
{
foreach (byte[] iov in msgHeader.Iov)
{
buffers[index++] = new ArraySegment<byte>(iov);
}
// Clear the length
msgHeader.Length = 0;
}
return buffers;
}
private static void UpdateMessages(out int vlen, BsdMMsgHdr message, int transferedSize)
{
int bytesLeft = transferedSize;
int index = 0;
while (bytesLeft > 0)
{
// First ensure we haven't finished all buffers
if (index >= message.Messages.Length)
{
break;
}
BsdMsgHdr msgHeader = message.Messages[index];
int possiblyTransferedBytes = 0;
foreach (byte[] iov in msgHeader.Iov)
{
possiblyTransferedBytes += iov.Length;
}
int storedBytes;
if (bytesLeft > possiblyTransferedBytes)
{
storedBytes = possiblyTransferedBytes;
index++;
}
else
{
storedBytes = bytesLeft;
}
msgHeader.Length = (uint)storedBytes;
bytesLeft -= storedBytes;
}
Debug.Assert(bytesLeft == 0);
vlen = index + 1;
}
// TODO: Find a way to support passing the timeout somehow without changing the socket ReceiveTimeout.
public LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout)
{
vlen = 0;
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
if (!CanSupportMMsgHdr(message))
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
return LinuxError.EOPNOTSUPP;
}
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
try
{
int receiveSize = Socket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (receiveSize > 0)
{
UpdateMessages(out vlen, message, receiveSize);
}
return WinSockHelper.ConvertError((WsaError)socketError);
}
catch (SocketException exception)
{
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
public LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags)
{
vlen = 0;
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
if (!CanSupportMMsgHdr(message))
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
return LinuxError.EOPNOTSUPP;
}
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
try
{
int sendSize = Socket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (sendSize > 0)
{
UpdateMessages(out vlen, message, sendSize);
}
return WinSockHelper.ConvertError((WsaError)socketError);
}
catch (SocketException exception)
{
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
}
}

View File

@ -38,12 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
bool isValidEvent = false;
bool isValidEvent = evnt.Data.InputEvents == 0;
errorEvents.Add(socket.Socket);
if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
{
readEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -51,7 +52,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
{
readEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -59,14 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
{
writeEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
if ((evnt.Data.InputEvents & PollEventTypeMask.Error) != 0)
{
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -93,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
PollEventTypeMask outputEvents = 0;
PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
if (errorEvents.Contains(socket))
{

View File

@ -0,0 +1,56 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
class BsdMMsgHdr
{
public BsdMsgHdr[] Messages { get; }
private BsdMMsgHdr(BsdMsgHdr[] messages)
{
Messages = messages;
}
public static LinuxError Serialize(Span<byte> rawData, BsdMMsgHdr message)
{
rawData[0] = 0x8;
rawData = rawData[1..];
for (int index = 0; index < message.Messages.Length; index++)
{
LinuxError res = BsdMsgHdr.Serialize(ref rawData, message.Messages[index]);
if (res != LinuxError.SUCCESS)
{
return res;
}
}
return LinuxError.SUCCESS;
}
public static LinuxError Deserialize(out BsdMMsgHdr message, ReadOnlySpan<byte> rawData, int vlen)
{
message = null;
BsdMsgHdr[] messages = new BsdMsgHdr[vlen];
// Skip "header" byte (Nintendo also ignore it)
rawData = rawData[1..];
for (int index = 0; index < messages.Length; index++)
{
LinuxError res = BsdMsgHdr.Deserialize(out messages[index], ref rawData);
if (res != LinuxError.SUCCESS)
{
return res;
}
}
message = new BsdMMsgHdr(messages);
return LinuxError.SUCCESS;
}
}
}

View File

@ -0,0 +1,212 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
class BsdMsgHdr
{
public byte[] Name { get; }
public byte[][] Iov { get; }
public byte[] Control { get; }
public BsdSocketFlags Flags { get; }
public uint Length;
private BsdMsgHdr(byte[] name, byte[][] iov, byte[] control, BsdSocketFlags flags, uint length)
{
Name = name;
Iov = iov;
Control = control;
Flags = flags;
Length = length;
}
public static LinuxError Serialize(ref Span<byte> rawData, BsdMsgHdr message)
{
int msgNameLength = message.Name == null ? 0 : message.Name.Length;
int iovCount = message.Iov == null ? 0 : message.Iov.Length;
int controlLength = message.Control == null ? 0 : message.Control.Length;
BsdSocketFlags flags = message.Flags;
if (!MemoryMarshal.TryWrite(rawData, ref msgNameLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (msgNameLength > 0)
{
if (rawData.Length < msgNameLength)
{
return LinuxError.EFAULT;
}
message.Name.CopyTo(rawData);
rawData = rawData[msgNameLength..];
}
if (!MemoryMarshal.TryWrite(rawData, ref iovCount))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (iovCount > 0)
{
for (int index = 0; index < iovCount; index++)
{
ulong iovLength = (ulong)message.Iov[index].Length;
if (!MemoryMarshal.TryWrite(rawData, ref iovLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(ulong)..];
if (iovLength > 0)
{
if ((ulong)rawData.Length < iovLength)
{
return LinuxError.EFAULT;
}
message.Iov[index].CopyTo(rawData);
rawData = rawData[(int)iovLength..];
}
}
}
if (!MemoryMarshal.TryWrite(rawData, ref controlLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (controlLength > 0)
{
if (rawData.Length < controlLength)
{
return LinuxError.EFAULT;
}
message.Control.CopyTo(rawData);
rawData = rawData[controlLength..];
}
if (!MemoryMarshal.TryWrite(rawData, ref flags))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(BsdSocketFlags)..];
if (!MemoryMarshal.TryWrite(rawData, ref message.Length))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
return LinuxError.SUCCESS;
}
public static LinuxError Deserialize(out BsdMsgHdr message, ref ReadOnlySpan<byte> rawData)
{
byte[] name = null;
byte[][] iov = null;
byte[] control = null;
message = null;
if (!MemoryMarshal.TryRead(rawData, out uint msgNameLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (msgNameLength > 0)
{
if (rawData.Length < msgNameLength)
{
return LinuxError.EFAULT;
}
name = rawData[..(int)msgNameLength].ToArray();
rawData = rawData[(int)msgNameLength..];
}
if (!MemoryMarshal.TryRead(rawData, out uint iovCount))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (iovCount > 0)
{
iov = new byte[iovCount][];
for (int index = 0; index < iov.Length; index++)
{
if (!MemoryMarshal.TryRead(rawData, out ulong iovLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(ulong)..];
if (iovLength > 0)
{
if ((ulong)rawData.Length < iovLength)
{
return LinuxError.EFAULT;
}
iov[index] = rawData[..(int)iovLength].ToArray();
rawData = rawData[(int)iovLength..];
}
}
}
if (!MemoryMarshal.TryRead(rawData, out uint controlLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (controlLength > 0)
{
if (rawData.Length < controlLength)
{
return LinuxError.EFAULT;
}
control = rawData[..(int)controlLength].ToArray();
rawData = rawData[(int)controlLength..];
}
if (!MemoryMarshal.TryRead(rawData, out BsdSocketFlags flags))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(BsdSocketFlags)..];
if (!MemoryMarshal.TryRead(rawData, out uint length))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
message = new BsdMsgHdr(name, iov, control, flags, length);
return LinuxError.SUCCESS;
}
}
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
public struct TimeVal
{
public ulong TvSec;
public ulong TvUsec;
}
}

View File

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
{
Length = (byte)Unsafe.SizeOf<Array4<byte>>();
Family = (byte)AddressFamily.InterNetwork;
Port = port;
Port = IPAddress.HostToNetworkOrder(port);
Address = new Array4<byte>();
address.TryWriteBytes(Address.AsSpan(), out _);

View File

@ -35,6 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private long _1msTicks;
private int _swapInterval;
private int _swapIntervalDelay;
private readonly object Lock = new object();
@ -91,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
else
{
_ticksPerFrame = Stopwatch.Frequency / (TargetFps / _swapInterval);
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
}
}
@ -321,9 +322,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
lastTicks = ticks;
if (_ticks >= _ticksPerFrame)
{
if (_swapIntervalDelay-- == 0)
{
Compose();
// When a frame is presented, delay the next one by its swap interval value.
_swapIntervalDelay = Math.Max(0, _swapInterval - 1);
}
_device.System?.SignalVsync();
// Apply a maximum bound of 3 frames to the tick remainder, in case some event causes Ryujinx to pause for a long time or messes with the timer.