Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ba95ee54ab | ||
|
4ce4299ca2 | ||
|
17620d18db | ||
|
9f1cf6458c | ||
|
67b4e63cff | ||
|
c05c688ee8 | ||
|
b2623dc27d | ||
|
5131b71437 | ||
|
7870423671 | ||
|
b72916fbc1 | ||
|
da073fce61 |
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||||
{
|
{
|
||||||
_stream = new MemoryStream();
|
_stream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
AllocResult = allocResult;
|
AllocResult = allocResult;
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -1033,7 +1034,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(opCode != BadOp, "Invalid opcode value.");
|
Debug.Assert(opCode != BadOp, "Invalid opcode value.");
|
||||||
|
|
||||||
if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
|
if ((flags & InstructionFlags.Evex) != 0 && HardwareCapabilities.SupportsEvexEncoding)
|
||||||
|
{
|
||||||
|
WriteEvexInst(dest, src1, src2, type, flags, opCode);
|
||||||
|
|
||||||
|
opCode &= 0xff;
|
||||||
|
}
|
||||||
|
else if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
|
||||||
{
|
{
|
||||||
// In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits.
|
// In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits.
|
||||||
|
|
||||||
@@ -1152,6 +1159,103 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WriteEvexInst(
|
||||||
|
Operand dest,
|
||||||
|
Operand src1,
|
||||||
|
Operand src2,
|
||||||
|
OperandType type,
|
||||||
|
InstructionFlags flags,
|
||||||
|
int opCode,
|
||||||
|
bool broadcast = false,
|
||||||
|
int registerWidth = 128,
|
||||||
|
int maskRegisterIdx = 0,
|
||||||
|
bool zeroElements = false)
|
||||||
|
{
|
||||||
|
int op1Idx = dest.GetRegister().Index;
|
||||||
|
int op2Idx = src1.GetRegister().Index;
|
||||||
|
int op3Idx = src2.GetRegister().Index;
|
||||||
|
|
||||||
|
WriteByte(0x62);
|
||||||
|
|
||||||
|
// P0
|
||||||
|
// Extend operand 1 register
|
||||||
|
bool r = (op1Idx & 8) == 0;
|
||||||
|
// Extend operand 3 register
|
||||||
|
bool x = (op3Idx & 16) == 0;
|
||||||
|
// Extend operand 3 register
|
||||||
|
bool b = (op3Idx & 8) == 0;
|
||||||
|
// Extend operand 1 register
|
||||||
|
bool rp = (op1Idx & 16) == 0;
|
||||||
|
// Escape code index
|
||||||
|
byte mm = 0b00;
|
||||||
|
|
||||||
|
switch ((ushort)(opCode >> 8))
|
||||||
|
{
|
||||||
|
case 0xf00: mm = 0b01; break;
|
||||||
|
case 0xf38: mm = 0b10; break;
|
||||||
|
case 0xf3a: mm = 0b11; break;
|
||||||
|
|
||||||
|
default: Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}."); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(r ? 0x80 : 0) |
|
||||||
|
(x ? 0x40 : 0) |
|
||||||
|
(b ? 0x20 : 0) |
|
||||||
|
(rp ? 0x10 : 0) |
|
||||||
|
mm));
|
||||||
|
|
||||||
|
// P1
|
||||||
|
// Specify 64-bit lane mode
|
||||||
|
bool w = Is64Bits(type);
|
||||||
|
// Operand 2 register index
|
||||||
|
byte vvvv = (byte)(~op2Idx & 0b1111);
|
||||||
|
// Opcode prefix
|
||||||
|
byte pp = (flags & InstructionFlags.PrefixMask) switch
|
||||||
|
{
|
||||||
|
InstructionFlags.Prefix66 => 0b01,
|
||||||
|
InstructionFlags.PrefixF3 => 0b10,
|
||||||
|
InstructionFlags.PrefixF2 => 0b11,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(w ? 0x80 : 0) |
|
||||||
|
(vvvv << 3) |
|
||||||
|
0b100 |
|
||||||
|
pp));
|
||||||
|
|
||||||
|
// P2
|
||||||
|
// Mask register determines what elements to zero, rather than what elements to merge
|
||||||
|
bool z = zeroElements;
|
||||||
|
// Specifies register-width
|
||||||
|
byte ll = 0b00;
|
||||||
|
switch (registerWidth)
|
||||||
|
{
|
||||||
|
case 128: ll = 0b00; break;
|
||||||
|
case 256: ll = 0b01; break;
|
||||||
|
case 512: ll = 0b10; break;
|
||||||
|
|
||||||
|
default: Debug.Fail($"Invalid EVEX vector register width {registerWidth}."); break;
|
||||||
|
}
|
||||||
|
// Embedded broadcast in the case of a memory operand
|
||||||
|
bool bcast = broadcast;
|
||||||
|
// Extend operand 2 register
|
||||||
|
bool vp = (op2Idx & 16) == 0;
|
||||||
|
// Mask register index
|
||||||
|
Debug.Assert(maskRegisterIdx < 8, $"Invalid mask register index {maskRegisterIdx}.");
|
||||||
|
byte aaa = (byte)(maskRegisterIdx & 0b111);
|
||||||
|
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(z ? 0x80 : 0) |
|
||||||
|
(ll << 5) |
|
||||||
|
(bcast ? 0x10 : 0) |
|
||||||
|
(vp ? 8 : 0) |
|
||||||
|
aaa));
|
||||||
|
}
|
||||||
|
|
||||||
private void WriteCompactInst(Operand operand, int opCode)
|
private void WriteCompactInst(Operand operand, int opCode)
|
||||||
{
|
{
|
||||||
int regIndex = operand.GetRegister().Index;
|
int regIndex = operand.GetRegister().Index;
|
||||||
@@ -1285,7 +1389,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
using var codeStream = new MemoryStream();
|
using var codeStream = MemoryStreamManager.Shared.GetStream();
|
||||||
var assembler = new Assembler(codeStream, HasRelocs);
|
var assembler = new Assembler(codeStream, HasRelocs);
|
||||||
|
|
||||||
bool hasRelocs = HasRelocs;
|
bool hasRelocs = HasRelocs;
|
||||||
|
@@ -20,6 +20,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Reg8Dest = 1 << 2,
|
Reg8Dest = 1 << 2,
|
||||||
RexW = 1 << 3,
|
RexW = 1 << 3,
|
||||||
Vex = 1 << 4,
|
Vex = 1 << 4,
|
||||||
|
Evex = 1 << 5,
|
||||||
|
|
||||||
PrefixBit = 16,
|
PrefixBit = 16,
|
||||||
PrefixMask = 7 << PrefixBit,
|
PrefixMask = 7 << PrefixBit,
|
||||||
@@ -278,6 +279,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vpternlogd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a25, InstructionFlags.Evex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
|
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
|
||||||
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex));
|
Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex));
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||||
{
|
{
|
||||||
_stream = new MemoryStream();
|
_stream = MemoryStreamManager.Shared.GetStream();
|
||||||
_blockLabels = new Operand[blocksCount];
|
_blockLabels = new Operand[blocksCount];
|
||||||
|
|
||||||
AllocResult = allocResult;
|
AllocResult = allocResult;
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
namespace ARMeilleure.CodeGen.X86
|
||||||
{
|
{
|
||||||
static class HardwareCapabilities
|
static class HardwareCapabilities
|
||||||
{
|
{
|
||||||
|
private delegate uint GetXcr0();
|
||||||
|
|
||||||
static HardwareCapabilities()
|
static HardwareCapabilities()
|
||||||
{
|
{
|
||||||
if (!X86Base.IsSupported)
|
if (!X86Base.IsSupported)
|
||||||
@@ -24,6 +28,28 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
||||||
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
|
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Xcr0InfoEax = (Xcr0FlagsEax)GetXcr0Eax();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GetXcr0Eax()
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> asmGetXcr0 = new byte[]
|
||||||
|
{
|
||||||
|
0x31, 0xc9, // xor ecx, ecx
|
||||||
|
0xf, 0x01, 0xd0, // xgetbv
|
||||||
|
0xc3, // ret
|
||||||
|
};
|
||||||
|
|
||||||
|
using MemoryBlock memGetXcr0 = new MemoryBlock((ulong)asmGetXcr0.Length);
|
||||||
|
|
||||||
|
memGetXcr0.Write(0, asmGetXcr0);
|
||||||
|
|
||||||
|
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
|
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
||||||
|
|
||||||
|
return fGetXcr0();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
@@ -44,6 +70,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Sse42 = 1 << 20,
|
Sse42 = 1 << 20,
|
||||||
Popcnt = 1 << 23,
|
Popcnt = 1 << 23,
|
||||||
Aes = 1 << 25,
|
Aes = 1 << 25,
|
||||||
|
Osxsave = 1 << 27,
|
||||||
Avx = 1 << 28,
|
Avx = 1 << 28,
|
||||||
F16c = 1 << 29
|
F16c = 1 << 29
|
||||||
}
|
}
|
||||||
@@ -52,7 +79,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public enum FeatureFlags7Ebx
|
public enum FeatureFlags7Ebx
|
||||||
{
|
{
|
||||||
Avx2 = 1 << 5,
|
Avx2 = 1 << 5,
|
||||||
Sha = 1 << 29
|
Avx512f = 1 << 16,
|
||||||
|
Avx512dq = 1 << 17,
|
||||||
|
Sha = 1 << 29,
|
||||||
|
Avx512bw = 1 << 30,
|
||||||
|
Avx512vl = 1 << 31
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
@@ -61,10 +92,21 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Gfni = 1 << 8,
|
Gfni = 1 << 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Xcr0FlagsEax
|
||||||
|
{
|
||||||
|
Sse = 1 << 1,
|
||||||
|
YmmHi128 = 1 << 2,
|
||||||
|
Opmask = 1 << 5,
|
||||||
|
ZmmHi256 = 1 << 6,
|
||||||
|
Hi16Zmm = 1 << 7
|
||||||
|
}
|
||||||
|
|
||||||
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
||||||
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
||||||
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
|
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
|
||||||
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
|
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
|
||||||
|
public static Xcr0FlagsEax Xcr0InfoEax { get; } = 0;
|
||||||
|
|
||||||
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
|
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
|
||||||
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
|
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
|
||||||
@@ -76,8 +118,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
||||||
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
||||||
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
||||||
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx);
|
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128);
|
||||||
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
||||||
|
public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Osxsave)
|
||||||
|
&& Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm);
|
||||||
|
public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F;
|
||||||
|
public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F;
|
||||||
|
public static bool SupportsAvx512Dq => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512dq) && SupportsAvx512F;
|
||||||
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
|
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
|
||||||
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
|
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
|
||||||
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
|
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
|
||||||
@@ -85,5 +132,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public static bool ForceLegacySse { get; set; }
|
public static bool ForceLegacySse { get; set; }
|
||||||
|
|
||||||
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
||||||
|
public static bool SupportsEvexEncoding => SupportsAvx512F && !ForceLegacySse;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -180,6 +180,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
|
||||||
|
Add(Intrinsic.X86Vpternlogd, new IntrinsicInfo(X86Instruction.Vpternlogd, IntrinsicType.TernaryImm));
|
||||||
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
|
||||||
}
|
}
|
||||||
|
@@ -219,6 +219,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Vfnmsub231sd,
|
Vfnmsub231sd,
|
||||||
Vfnmsub231ss,
|
Vfnmsub231ss,
|
||||||
Vpblendvb,
|
Vpblendvb,
|
||||||
|
Vpternlogd,
|
||||||
Xor,
|
Xor,
|
||||||
Xorpd,
|
Xorpd,
|
||||||
Xorps,
|
Xorps,
|
||||||
|
@@ -254,7 +254,22 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Not_V(ArmEmitterContext context)
|
public static void Not_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse2)
|
if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, n, Const(~0b10101010));
|
||||||
|
|
||||||
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
|
{
|
||||||
|
res = context.VectorZeroUpper64(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVec(op.Rd), res);
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
@@ -283,6 +298,22 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV);
|
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||||
|
|
||||||
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
|
{
|
||||||
|
res = context.VectorZeroUpper64(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVec(op.Rd), res);
|
||||||
|
}
|
||||||
else if (Optimizations.UseSse2)
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||||
|
@@ -151,6 +151,13 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m));
|
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m));
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||||
|
{
|
||||||
|
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||||
|
});
|
||||||
|
}
|
||||||
else if (Optimizations.UseSse2)
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
Operand mask = context.VectorOne();
|
Operand mask = context.VectorOne();
|
||||||
|
@@ -34,7 +34,14 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Vmvn_I(ArmEmitterContext context)
|
public static void Vmvn_I(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse2)
|
if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||||
|
{
|
||||||
|
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, op1, op1, Const(0b01010101));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpSimd32(context, (op1) =>
|
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||||
{
|
{
|
||||||
|
@@ -173,6 +173,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Vfnmadd231ss,
|
X86Vfnmadd231ss,
|
||||||
X86Vfnmsub231sd,
|
X86Vfnmsub231sd,
|
||||||
X86Vfnmsub231ss,
|
X86Vfnmsub231ss,
|
||||||
|
X86Vpternlogd,
|
||||||
X86Xorpd,
|
X86Xorpd,
|
||||||
X86Xorps,
|
X86Xorps,
|
||||||
|
|
||||||
|
@@ -23,6 +23,10 @@ namespace ARMeilleure
|
|||||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
public static bool UseSse42IfAvailable { get; set; } = true;
|
||||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
public static bool UsePopCntIfAvailable { get; set; } = true;
|
||||||
public static bool UseAvxIfAvailable { get; set; } = true;
|
public static bool UseAvxIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512FIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512VlIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512BwIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512DqIfAvailable { get; set; } = true;
|
||||||
public static bool UseF16cIfAvailable { get; set; } = true;
|
public static bool UseF16cIfAvailable { get; set; } = true;
|
||||||
public static bool UseFmaIfAvailable { get; set; } = true;
|
public static bool UseFmaIfAvailable { get; set; } = true;
|
||||||
public static bool UseAesniIfAvailable { get; set; } = true;
|
public static bool UseAesniIfAvailable { get; set; } = true;
|
||||||
@@ -47,11 +51,18 @@ namespace ARMeilleure
|
|||||||
internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
|
internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
|
||||||
internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
|
internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
|
||||||
internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512F => UseAvx512FIfAvailable && X86HardwareCapabilities.SupportsAvx512F && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Vl => UseAvx512VlIfAvailable && X86HardwareCapabilities.SupportsAvx512Vl && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Bw => UseAvx512BwIfAvailable && X86HardwareCapabilities.SupportsAvx512Bw && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Dq => UseAvx512DqIfAvailable && X86HardwareCapabilities.SupportsAvx512Dq && !ForceLegacySse;
|
||||||
internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
|
internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
|
||||||
internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
|
internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
|
||||||
internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
|
internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
|
||||||
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
|
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
|
||||||
internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
|
internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
|
||||||
internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
|
internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
|
||||||
|
|
||||||
|
internal static bool UseAvx512Ortho => UseAvx512F && UseAvx512Vl;
|
||||||
|
internal static bool UseAvx512OrthoFloat => UseAvx512Ortho && UseAvx512Dq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ using ARMeilleure.Memory;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -29,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 4484; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 4485; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
@@ -150,10 +151,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private void InitializeCarriers()
|
private void InitializeCarriers()
|
||||||
{
|
{
|
||||||
_infosStream = new MemoryStream();
|
_infosStream = MemoryStreamManager.Shared.GetStream();
|
||||||
_codesList = new List<byte[]>();
|
_codesList = new List<byte[]>();
|
||||||
_relocsStream = new MemoryStream();
|
_relocsStream = MemoryStreamManager.Shared.GetStream();
|
||||||
_unwindInfosStream = new MemoryStream();
|
_unwindInfosStream = MemoryStreamManager.Shared.GetStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeCarriers()
|
private void DisposeCarriers()
|
||||||
@@ -968,6 +969,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap,
|
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap,
|
||||||
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2,
|
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2,
|
||||||
(ulong)Arm64HardwareCapabilities.MacOsFeatureInfo,
|
(ulong)Arm64HardwareCapabilities.MacOsFeatureInfo,
|
||||||
|
0,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
@@ -976,11 +978,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
(ulong)X86HardwareCapabilities.FeatureInfo1Ecx,
|
(ulong)X86HardwareCapabilities.FeatureInfo1Ecx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo1Edx,
|
(ulong)X86HardwareCapabilities.FeatureInfo1Edx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo7Ebx,
|
(ulong)X86HardwareCapabilities.FeatureInfo7Ebx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo7Ecx);
|
(ulong)X86HardwareCapabilities.FeatureInfo7Ecx,
|
||||||
|
(ulong)X86HardwareCapabilities.Xcr0InfoEax);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new FeatureInfo(0, 0, 0, 0);
|
return new FeatureInfo(0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,7 +1004,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return osPlatform;
|
return osPlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 78*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
|
||||||
private struct OuterHeader
|
private struct OuterHeader
|
||||||
{
|
{
|
||||||
public ulong Magic;
|
public ulong Magic;
|
||||||
@@ -1033,8 +1036,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 32*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 40*/)]
|
||||||
private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3);
|
private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3, ulong FeatureInfo4);
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
|
||||||
private struct InnerHeader
|
private struct InnerHeader
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -182,7 +183,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||||
|
|
||||||
@@ -274,7 +275,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
outerHeader.SetHeaderHash();
|
outerHeader.SetHeaderHash();
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||||
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -12,19 +12,5 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
|
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
|
||||||
|
|
||||||
writer.Write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Write(this BinaryWriter writer, UInt128 value)
|
|
||||||
{
|
|
||||||
writer.Write((ulong)value);
|
|
||||||
writer.Write((ulong)(value >> 64));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
Normal file
28
Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class BinaryWriterExtensions
|
||||||
|
{
|
||||||
|
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
||||||
|
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this BinaryWriter writer, UInt128 value)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)value);
|
||||||
|
writer.Write((ulong)(value >> 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this BinaryWriter writer, MemoryStream stream)
|
||||||
|
{
|
||||||
|
stream.CopyTo(writer.BaseStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
Ryujinx.Common/Extensions/StreamExtensions.cs
Normal file
138
Ryujinx.Common/Extensions/StreamExtensions.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class StreamExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a <cref="ReadOnlySpan<int>" /> to this stream.
|
||||||
|
///
|
||||||
|
/// This default implementation converts each buffer value to a stack-allocated
|
||||||
|
/// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="buffer">The buffer of values to be written</param>
|
||||||
|
public static void Write(this Stream stream, ReadOnlySpan<int> buffer)
|
||||||
|
{
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer);
|
||||||
|
stream.Write(byteBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span<byte> byteBuffer = stackalloc byte[sizeof(int)];
|
||||||
|
|
||||||
|
foreach (int value in buffer)
|
||||||
|
{
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value);
|
||||||
|
stream.Write(byteBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a four-byte signed integer to this stream. The current position
|
||||||
|
/// of the stream is advanced by four.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, int value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(int)];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes an eight-byte signed integer to this stream. The current position
|
||||||
|
/// of the stream is advanced by eight.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, long value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(long)];
|
||||||
|
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// Writes a four-byte unsigned integer to this stream. The current position
|
||||||
|
// of the stream is advanced by four.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, uint value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes an eight-byte unsigned integer to this stream. The current
|
||||||
|
/// position of the stream is advanced by eight.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||||
|
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the contents of source to stream by calling source.CopyTo(stream).
|
||||||
|
/// Provides consistency with other Stream.Write methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="source">The stream to be read from</param>
|
||||||
|
public static void Write(this Stream stream, Stream source)
|
||||||
|
{
|
||||||
|
source.CopyTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a sequence of bytes to the Stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to.</param>
|
||||||
|
/// <param name="value">The byte to be written</param>
|
||||||
|
/// <param name="count">The number of times the value should be written</param>
|
||||||
|
public static void WriteByte(this Stream stream, byte value, int count)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int BlockSize = 16;
|
||||||
|
|
||||||
|
int blockCount = count / BlockSize;
|
||||||
|
if (blockCount > 0)
|
||||||
|
{
|
||||||
|
Span<byte> span = stackalloc byte[BlockSize];
|
||||||
|
span.Fill(value);
|
||||||
|
for (int x = 0; x < blockCount; x++)
|
||||||
|
{
|
||||||
|
stream.Write(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nonBlockBytes = count % BlockSize;
|
||||||
|
for (int x = 0; x < nonBlockBytes; x++)
|
||||||
|
{
|
||||||
|
stream.WriteByte(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
Ryujinx.Common/Memory/MemoryStreamManager.cs
Normal file
99
Ryujinx.Common/Memory/MemoryStreamManager.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public static class MemoryStreamManager
|
||||||
|
{
|
||||||
|
private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
|
||||||
|
/// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
|
||||||
|
/// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast.
|
||||||
|
/// </summary>
|
||||||
|
public static class Shared
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream()
|
||||||
|
=> new RecyclableMemoryStream(_shared);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(byte[] buffer)
|
||||||
|
=> GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer)
|
||||||
|
=> GetStream(Guid.NewGuid(), null, buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
|
||||||
|
/// <param name="tag">A tag which can be used to track the source of the stream</param>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
RecyclableMemoryStream stream = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
|
||||||
|
stream.Write(buffer);
|
||||||
|
stream.Position = 0;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks>
|
||||||
|
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
|
||||||
|
/// <param name="tag">A tag which can be used to track the source of the stream</param>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <param name="offset">The offset from the start of the buffer to copy from</param>
|
||||||
|
/// <param name="count">The number of bytes to copy from the buffer</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
RecyclableMemoryStream stream = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new RecyclableMemoryStream(_shared, id, tag, count);
|
||||||
|
stream.Write(buffer, offset, count);
|
||||||
|
stream.Position = 0;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="System.Management" />
|
<PackageReference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -38,12 +40,7 @@ namespace Ryujinx.Common
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var mem = new MemoryStream())
|
return StreamUtils.StreamToBytes(stream);
|
||||||
{
|
|
||||||
stream.CopyTo(mem);
|
|
||||||
|
|
||||||
return mem.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +53,7 @@ namespace Ryujinx.Common
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var mem = new MemoryStream())
|
return await StreamUtils.StreamToBytesAsync(stream);
|
||||||
{
|
|
||||||
await stream.CopyToAsync(mem);
|
|
||||||
|
|
||||||
return mem.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
using System.IO;
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
@@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
public static byte[] StreamToBytes(Stream input)
|
public static byte[] StreamToBytes(Stream input)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
input.CopyTo(stream);
|
input.CopyTo(stream);
|
||||||
|
|
||||||
return stream.ToArray();
|
return stream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
|
{
|
||||||
|
await input.CopyToAsync(stream, cancellationToken);
|
||||||
|
|
||||||
|
return stream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
using Ryujinx.Memory;
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -40,7 +42,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
|
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
|
||||||
{
|
{
|
||||||
@@ -54,7 +56,7 @@ namespace Ryujinx.Cpu
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.ASCII.GetString(ms.ToArray());
|
return Encoding.ASCII.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,12 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
||||||
|
|
||||||
BufferHandle CreateBuffer(int size);
|
BufferHandle CreateBuffer(int size, BufferHandle storageHint);
|
||||||
|
|
||||||
|
BufferHandle CreateBuffer(int size)
|
||||||
|
{
|
||||||
|
return CreateBuffer(size, BufferHandle.Null);
|
||||||
|
}
|
||||||
|
|
||||||
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
|
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
|
||||||
|
|
||||||
@@ -26,7 +31,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void DeleteBuffer(BufferHandle buffer);
|
void DeleteBuffer(BufferHandle buffer);
|
||||||
|
|
||||||
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
||||||
|
|
||||||
Capabilities GetCapabilities();
|
Capabilities GetCapabilities();
|
||||||
ulong GetCurrentSync();
|
ulong GetCurrentSync();
|
||||||
|
@@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
||||||
|
|
||||||
ReadOnlySpan<byte> GetData();
|
PinnedSpan<byte> GetData();
|
||||||
ReadOnlySpan<byte> GetData(int layer, int level);
|
PinnedSpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
void SetData(SpanOrArray<byte> data);
|
void SetData(SpanOrArray<byte> data);
|
||||||
void SetData(SpanOrArray<byte> data, int layer, int level);
|
void SetData(SpanOrArray<byte> data, int layer, int level);
|
||||||
|
@@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer
|
|||||||
|
|
||||||
public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size);
|
PinnedSpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size);
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,16 +5,25 @@
|
|||||||
public CommandType CommandType => CommandType.CreateBuffer;
|
public CommandType CommandType => CommandType.CreateBuffer;
|
||||||
private BufferHandle _threadedHandle;
|
private BufferHandle _threadedHandle;
|
||||||
private int _size;
|
private int _size;
|
||||||
|
private BufferHandle _storageHint;
|
||||||
|
|
||||||
public void Set(BufferHandle threadedHandle, int size)
|
public void Set(BufferHandle threadedHandle, int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
_threadedHandle = threadedHandle;
|
_threadedHandle = threadedHandle;
|
||||||
_size = size;
|
_size = size;
|
||||||
|
_storageHint = storageHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size));
|
BufferHandle hint = BufferHandle.Null;
|
||||||
|
|
||||||
|
if (command._storageHint != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
hint = threaded.Buffers.MapBuffer(command._storageHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, hint));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,9 +18,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
|||||||
|
|
||||||
public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData();
|
PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData();
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,9 +22,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
|||||||
|
|
||||||
public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level);
|
PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level);
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Model
|
|
||||||
{
|
|
||||||
unsafe struct PinnedSpan<T> where T : unmanaged
|
|
||||||
{
|
|
||||||
private void* _ptr;
|
|
||||||
private int _size;
|
|
||||||
|
|
||||||
public PinnedSpan(ReadOnlySpan<T> span)
|
|
||||||
{
|
|
||||||
_ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
|
|
||||||
_size = span.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<T> Get()
|
|
||||||
{
|
|
||||||
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
return newTex;
|
return newTex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
if (_renderer.IsGpuThread())
|
if (_renderer.IsGpuThread())
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
_renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box));
|
_renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box));
|
||||||
_renderer.InvokeCommand();
|
_renderer.InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
if (_renderer.IsGpuThread())
|
if (_renderer.IsGpuThread())
|
||||||
{
|
{
|
||||||
@@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
_renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level);
|
_renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level);
|
||||||
_renderer.InvokeCommand();
|
_renderer.InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -265,10 +265,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
BufferHandle handle = Buffers.CreateBufferHandle();
|
BufferHandle handle = Buffers.CreateBufferHandle();
|
||||||
New<CreateBufferCommand>().Set(handle, size);
|
New<CreateBufferCommand>().Set(handle, size, storageHint);
|
||||||
QueueCommand();
|
QueueCommand();
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
@@ -329,7 +329,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
QueueCommand();
|
QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
if (IsGpuThread())
|
if (IsGpuThread())
|
||||||
{
|
{
|
||||||
@@ -337,7 +337,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box));
|
New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box));
|
||||||
InvokeCommand();
|
InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
53
Ryujinx.Graphics.GAL/PinnedSpan.cs
Normal file
53
Ryujinx.Graphics.GAL/PinnedSpan.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged
|
||||||
|
{
|
||||||
|
private void* _ptr;
|
||||||
|
private int _size;
|
||||||
|
private Action _disposeAction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new PinnedSpan from an existing ReadOnlySpan. The span *must* be pinned in memory.
|
||||||
|
/// The data must be guaranteed to live until disposeAction is called.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">Existing span</param>
|
||||||
|
/// <param name="disposeAction">Action to call on dispose</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a dispose action is not provided, it is safe to assume the resource will be available until the next call.
|
||||||
|
/// </remarks>
|
||||||
|
public static PinnedSpan<T> UnsafeFromSpan(ReadOnlySpan<T> span, Action disposeAction = null)
|
||||||
|
{
|
||||||
|
return new PinnedSpan<T>(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)), span.Length, disposeAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new PinnedSpan from an existing unsafe region. The data must be guaranteed to live until disposeAction is called.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">Pointer to the region</param>
|
||||||
|
/// <param name="size">The total items of T the region contains</param>
|
||||||
|
/// <param name="disposeAction">Action to call on dispose</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a dispose action is not provided, it is safe to assume the resource will be available until the next call.
|
||||||
|
/// </remarks>
|
||||||
|
public PinnedSpan(void* ptr, int size, Action disposeAction = null)
|
||||||
|
{
|
||||||
|
_ptr = ptr;
|
||||||
|
_size = size;
|
||||||
|
_disposeAction = disposeAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<T> Get()
|
||||||
|
{
|
||||||
|
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_disposeAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
texture._viewStorage = this;
|
texture._viewStorage = this;
|
||||||
|
|
||||||
Group.UpdateViews(_views);
|
Group.UpdateViews(_views, texture);
|
||||||
|
|
||||||
if (texture.Group != null && texture.Group != Group)
|
if (texture.Group != null && texture.Group != Group)
|
||||||
{
|
{
|
||||||
@@ -384,6 +384,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_views.Remove(texture);
|
_views.Remove(texture);
|
||||||
|
|
||||||
|
Group.RemoveView(texture);
|
||||||
|
|
||||||
texture._viewStorage = texture;
|
texture._viewStorage = texture;
|
||||||
|
|
||||||
DecrementReferenceCount();
|
DecrementReferenceCount();
|
||||||
@@ -1020,13 +1022,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// This method should be used to retrieve data that was modified by the host GPU.
|
/// This method should be used to retrieve data that was modified by the host GPU.
|
||||||
/// This is not cheap, avoid doing that unless strictly needed.
|
/// This is not cheap, avoid doing that unless strictly needed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
|
/// <param name="output">An output span to place the texture data into</param>
|
||||||
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
||||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||||
/// <returns>The span containing the texture data</returns>
|
private void GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
|
||||||
private ReadOnlySpan<byte> GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
PinnedSpan<byte> data;
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
@@ -1052,9 +1053,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = ConvertFromHostCompatibleFormat(output, data);
|
ConvertFromHostCompatibleFormat(output, data.Get());
|
||||||
|
|
||||||
return data;
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1069,10 +1070,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="level">The level of the texture to flush</param>
|
/// <param name="level">The level of the texture to flush</param>
|
||||||
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
||||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||||
/// <returns>The span containing the texture data</returns>
|
public void GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
|
||||||
public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
PinnedSpan<byte> data;
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
@@ -1098,9 +1098,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = ConvertFromHostCompatibleFormat(output, data, level, true);
|
ConvertFromHostCompatibleFormat(output, data.Get(), level, true);
|
||||||
|
|
||||||
return data;
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1473,8 +1473,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
MultiRange otherRange = texture.Range;
|
MultiRange otherRange = texture.Range;
|
||||||
|
|
||||||
IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.GetSlice((ulong)region.Offset, (ulong)region.Size));
|
IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.Slice((ulong)region.Offset, (ulong)region.Size));
|
||||||
IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.GetSlice((ulong)region.Offset, (ulong)region.Size));
|
IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.Slice((ulong)region.Offset, (ulong)region.Size));
|
||||||
|
|
||||||
foreach (MultiRange region in regions)
|
foreach (MultiRange region in regions)
|
||||||
{
|
{
|
||||||
|
@@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
|
int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
|
||||||
int size = endOffset - spanBase;
|
int size = endOffset - spanBase;
|
||||||
|
|
||||||
dataSpan = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)spanBase, (ulong)size));
|
dataSpan = _physicalMemory.GetSpan(Storage.Range.Slice((ulong)spanBase, (ulong)size));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
||||||
@@ -473,7 +473,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size);
|
int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size);
|
||||||
int size = endOffset - offset;
|
int size = endOffset - offset;
|
||||||
|
|
||||||
using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.GetSlice((ulong)offset, (ulong)size), tracked);
|
using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
|
||||||
|
|
||||||
Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
|
Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
|
||||||
}
|
}
|
||||||
@@ -989,7 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Update the views in this texture group, rebuilding the memory tracking if required.
|
/// Update the views in this texture group, rebuilding the memory tracking if required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="views">The views list of the storage texture</param>
|
/// <param name="views">The views list of the storage texture</param>
|
||||||
public void UpdateViews(List<Texture> views)
|
/// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
|
||||||
|
public void UpdateViews(List<Texture> views, Texture texture)
|
||||||
{
|
{
|
||||||
// This is saved to calculate overlapping views for each handle.
|
// This is saved to calculate overlapping views for each handle.
|
||||||
_views = views;
|
_views = views;
|
||||||
@@ -1027,17 +1028,44 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (!regionsRebuilt)
|
if (!regionsRebuilt)
|
||||||
{
|
{
|
||||||
// Must update the overlapping views on all handles, but only if they were not just recreated.
|
if (texture != null)
|
||||||
|
|
||||||
foreach (TextureGroupHandle handle in _handles)
|
|
||||||
{
|
{
|
||||||
handle.RecalculateOverlaps(this, views);
|
int offset = FindOffset(texture);
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.AddOverlap(offset, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Must update the overlapping views on all handles, but only if they were not just recreated.
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.RecalculateOverlaps(this, views);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalAllDirty();
|
SignalAllDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a view from the group, removing it from all overlap lists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="view">View to remove from the group</param>
|
||||||
|
public void RemoveView(Texture view)
|
||||||
|
{
|
||||||
|
int offset = FindOffset(view);
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.RemoveOverlap(offset, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inherit handle state from an old set of handles, such as modified and dirty flags.
|
/// Inherit handle state from an old set of handles, such as modified and dirty flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1391,13 +1419,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
for (int i = 0; i < _allOffsets.Length; i++)
|
for (int i = 0; i < _allOffsets.Length; i++)
|
||||||
{
|
{
|
||||||
(int layer, int level) = GetLayerLevelForView(i);
|
(int layer, int level) = GetLayerLevelForView(i);
|
||||||
MultiRange handleRange = Storage.Range.GetSlice((ulong)_allOffsets[i], 1);
|
MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1);
|
||||||
ulong handleBase = handleRange.GetSubRange(0).Address;
|
ulong handleBase = handleRange.GetSubRange(0).Address;
|
||||||
|
|
||||||
for (int j = 0; j < other._handles.Length; j++)
|
for (int j = 0; j < other._handles.Length; j++)
|
||||||
{
|
{
|
||||||
(int otherLayer, int otherLevel) = other.GetLayerLevelForView(j);
|
(int otherLayer, int otherLevel) = other.GetLayerLevelForView(j);
|
||||||
MultiRange otherHandleRange = other.Storage.Range.GetSlice((ulong)other._allOffsets[j], 1);
|
MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1);
|
||||||
ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address;
|
ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address;
|
||||||
|
|
||||||
if (handleBase == otherHandleBase)
|
if (handleBase == otherHandleBase)
|
||||||
@@ -1474,7 +1502,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Handles list is not modified by another thread, only replaced, so this is thread safe.
|
// Handles list is not modified by another thread, only replaced, so this is thread safe.
|
||||||
// Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory.
|
// Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory.
|
||||||
|
|
||||||
MultiRange subRange = Storage.Range.GetSlice((ulong)handle.Offset, (ulong)handle.Size);
|
MultiRange subRange = Storage.Range.Slice((ulong)handle.Offset, (ulong)handle.Size);
|
||||||
|
|
||||||
if (range.OverlapsWith(subRange))
|
if (range.OverlapsWith(subRange))
|
||||||
{
|
{
|
||||||
|
@@ -159,6 +159,42 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a single texture view as an overlap if its range overlaps.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The offset of the view in the group</param>
|
||||||
|
/// <param name="view">The texture to add as an overlap</param>
|
||||||
|
public void AddOverlap(int offset, Texture view)
|
||||||
|
{
|
||||||
|
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
||||||
|
|
||||||
|
if (OverlapsWith(offset, (int)view.Size))
|
||||||
|
{
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
Overlaps.Add(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a single texture view as an overlap if its range overlaps.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The offset of the view in the group</param>
|
||||||
|
/// <param name="view">The texture to add as an overlap</param>
|
||||||
|
public void RemoveOverlap(int offset, Texture view)
|
||||||
|
{
|
||||||
|
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
||||||
|
|
||||||
|
if (OverlapsWith(offset, (int)view.Size))
|
||||||
|
{
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
Overlaps.Remove(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
|
/// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
Handle = context.Renderer.CreateBuffer((int)size);
|
Handle = context.Renderer.CreateBuffer((int)size, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null);
|
||||||
|
|
||||||
_useGranular = size > GranularBufferThreshold;
|
_useGranular = size > GranularBufferThreshold;
|
||||||
|
|
||||||
@@ -415,10 +415,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
int offset = (int)(address - Address);
|
int offset = (int)(address - Address);
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
||||||
|
|
||||||
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
||||||
_physicalMemory.WriteUntracked(address, data);
|
_physicalMemory.WriteUntracked(address, data.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
@@ -11,16 +13,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
public static byte[] Pack(ShaderSource[] sources)
|
public static byte[] Pack(ShaderSource[] sources)
|
||||||
{
|
{
|
||||||
using MemoryStream output = new MemoryStream();
|
using MemoryStream output = MemoryStreamManager.Shared.GetStream();
|
||||||
using BinaryWriter writer = new BinaryWriter(output);
|
|
||||||
|
|
||||||
writer.Write(sources.Length);
|
output.Write(sources.Length);
|
||||||
|
|
||||||
for (int i = 0; i < sources.Length; i++)
|
foreach (ShaderSource source in sources)
|
||||||
{
|
{
|
||||||
writer.Write((int)sources[i].Stage);
|
output.Write((int)source.Stage);
|
||||||
writer.Write(sources[i].BinaryCode.Length);
|
output.Write(source.BinaryCode.Length);
|
||||||
writer.Write(sources[i].BinaryCode);
|
output.Write(source.BinaryCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
|
@@ -55,11 +55,14 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
(IntPtr)size);
|
(IntPtr)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe ReadOnlySpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size)
|
public static unsafe PinnedSpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
|
// Data in the persistent buffer and host array is guaranteed to be available
|
||||||
|
// until the next time the host thread requests data.
|
||||||
|
|
||||||
if (HwCapabilities.UsePersistentBufferForFlush)
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
||||||
{
|
{
|
||||||
return renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size);
|
return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -69,7 +72,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target);
|
GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target);
|
||||||
|
|
||||||
return new ReadOnlySpan<byte>(target.ToPointer(), size);
|
return new PinnedSpan<byte>(target.ToPointer(), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,12 +39,12 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
return Buffer.GetData(_renderer, _buffer, _bufferOffset, _bufferSize);
|
return Buffer.GetData(_renderer, _buffer, _bufferOffset, _bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
return GetData();
|
return GetData();
|
||||||
}
|
}
|
||||||
|
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData()
|
public unsafe PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.GetLevelsClamped();
|
||||||
@@ -196,16 +196,16 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
data = FormatConverter.ConvertD24S8ToS8D24(data);
|
data = FormatConverter.ConvertD24S8ToS8D24(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return PinnedSpan<byte>.UnsafeFromSpan(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
|
public unsafe PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
int size = Info.GetMipSize(level);
|
int size = Info.GetMipSize(level);
|
||||||
|
|
||||||
if (HwCapabilities.UsePersistentBufferForFlush)
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
||||||
{
|
{
|
||||||
return _renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(_renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -213,7 +213,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
int offset = WriteTo2D(target, layer, level);
|
int offset = WriteTo2D(target, layer, level);
|
||||||
|
|
||||||
return new ReadOnlySpan<byte>(target.ToPointer(), size).Slice(offset);
|
return new PinnedSpan<byte>((byte*)target.ToPointer() + offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
ResourcePool = new ResourcePool();
|
ResourcePool = new ResourcePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
BufferCount++;
|
BufferCount++;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
return new HardwareInfo(GpuVendor, GpuRenderer);
|
return new HardwareInfo(GpuVendor, GpuRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
return Buffer.GetData(this, buffer, offset, size);
|
return Buffer.GetData(this, buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
@@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||||||
{
|
{
|
||||||
result = Marshal.ReadInt64(_bufferMap);
|
result = Marshal.ReadInt64(_bufferMap);
|
||||||
|
|
||||||
return WaitingForValue(result);
|
return !WaitingForValue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long AwaitResult(AutoResetEvent wakeSignal = null)
|
public long AwaitResult(AutoResetEvent wakeSignal = null)
|
||||||
|
12
Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
Normal file
12
Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
internal enum BufferAllocationType
|
||||||
|
{
|
||||||
|
Auto = 0,
|
||||||
|
|
||||||
|
HostMappedNoCache,
|
||||||
|
HostMapped,
|
||||||
|
DeviceLocal,
|
||||||
|
DeviceLocalMapped
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,10 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
|
||||||
@@ -11,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
private const int MaxUpdateBufferSize = 0x10000;
|
private const int MaxUpdateBufferSize = 0x10000;
|
||||||
|
|
||||||
|
private const int SetCountThreshold = 100;
|
||||||
|
private const int WriteCountThreshold = 50;
|
||||||
|
private const int FlushCountThreshold = 5;
|
||||||
|
|
||||||
|
public const int DeviceLocalSizeThreshold = 256 * 1024; // 256kb
|
||||||
|
|
||||||
public const AccessFlags DefaultAccessFlags =
|
public const AccessFlags DefaultAccessFlags =
|
||||||
AccessFlags.IndirectCommandReadBit |
|
AccessFlags.IndirectCommandReadBit |
|
||||||
AccessFlags.ShaderReadBit |
|
AccessFlags.ShaderReadBit |
|
||||||
@@ -21,10 +30,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly MemoryAllocation _allocation;
|
private MemoryAllocation _allocation;
|
||||||
private readonly Auto<DisposableBuffer> _buffer;
|
private Auto<DisposableBuffer> _buffer;
|
||||||
private readonly Auto<MemoryAllocation> _allocationAuto;
|
private Auto<MemoryAllocation> _allocationAuto;
|
||||||
private readonly ulong _bufferHandle;
|
private ulong _bufferHandle;
|
||||||
|
|
||||||
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
|
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
|
||||||
|
|
||||||
@@ -32,11 +41,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private IntPtr _map;
|
private IntPtr _map;
|
||||||
|
|
||||||
private readonly MultiFenceHolder _waitable;
|
private MultiFenceHolder _waitable;
|
||||||
|
|
||||||
private bool _lastAccessIsWrite;
|
private bool _lastAccessIsWrite;
|
||||||
|
|
||||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size)
|
private BufferAllocationType _baseType;
|
||||||
|
private BufferAllocationType _currentType;
|
||||||
|
private bool _swapQueued;
|
||||||
|
|
||||||
|
public BufferAllocationType DesiredType { get; private set; }
|
||||||
|
|
||||||
|
private int _setCount;
|
||||||
|
private int _writeCount;
|
||||||
|
private int _flushCount;
|
||||||
|
private int _flushTemp;
|
||||||
|
|
||||||
|
private ReaderWriterLock _flushLock;
|
||||||
|
private FenceHolder _flushFence;
|
||||||
|
private int _flushWaiting;
|
||||||
|
|
||||||
|
private List<Action> _swapActions;
|
||||||
|
|
||||||
|
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
@@ -47,9 +73,153 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_bufferHandle = buffer.Handle;
|
_bufferHandle = buffer.Handle;
|
||||||
Size = size;
|
Size = size;
|
||||||
_map = allocation.HostPointer;
|
_map = allocation.HostPointer;
|
||||||
|
|
||||||
|
_baseType = type;
|
||||||
|
_currentType = currentType;
|
||||||
|
DesiredType = currentType;
|
||||||
|
|
||||||
|
_flushLock = new ReaderWriterLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size)
|
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
|
||||||
|
{
|
||||||
|
if (_swapQueued && DesiredType != _currentType)
|
||||||
|
{
|
||||||
|
// Only swap if the buffer is not used in any queued command buffer.
|
||||||
|
bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);
|
||||||
|
|
||||||
|
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld)
|
||||||
|
{
|
||||||
|
var currentAllocation = _allocationAuto;
|
||||||
|
var currentBuffer = _buffer;
|
||||||
|
IntPtr currentMap = _map;
|
||||||
|
|
||||||
|
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType);
|
||||||
|
|
||||||
|
if (buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
ClearFlushFence();
|
||||||
|
|
||||||
|
_waitable = new MultiFenceHolder(Size);
|
||||||
|
|
||||||
|
_allocation = allocation;
|
||||||
|
_allocationAuto = new Auto<MemoryAllocation>(allocation);
|
||||||
|
_buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), _waitable, _allocationAuto);
|
||||||
|
_bufferHandle = buffer.Handle;
|
||||||
|
_map = allocation.HostPointer;
|
||||||
|
|
||||||
|
if (_map != IntPtr.Zero && currentMap != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
// Copy data directly. Readbacks don't have to wait if this is done.
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
new Span<byte>((void*)currentMap, Size).CopyTo(new Span<byte>((void*)_map, Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cbs == null)
|
||||||
|
{
|
||||||
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBufferScoped cbsV = cbs.Value;
|
||||||
|
|
||||||
|
Copy(_gd, cbsV, currentBuffer, _buffer, 0, 0, Size);
|
||||||
|
|
||||||
|
// Need to wait for the data to reach the new buffer before data can be flushed.
|
||||||
|
|
||||||
|
_flushFence = _gd.CommandBufferPool.GetFence(cbsV.CommandBufferIndex);
|
||||||
|
_flushFence.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug?.PrintMsg(LogClass.Gpu, $"Converted {Size} buffer {_currentType} to {resultType}");
|
||||||
|
|
||||||
|
_currentType = resultType;
|
||||||
|
|
||||||
|
if (_swapActions != null)
|
||||||
|
{
|
||||||
|
foreach (var action in _swapActions)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapActions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBuffer.Dispose();
|
||||||
|
currentAllocation.Dispose();
|
||||||
|
|
||||||
|
_gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer);
|
||||||
|
|
||||||
|
_flushLock.ReleaseWriterLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConsiderBackingSwap()
|
||||||
|
{
|
||||||
|
if (_baseType == BufferAllocationType.Auto)
|
||||||
|
{
|
||||||
|
if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold)
|
||||||
|
{
|
||||||
|
if (_flushCount > 0 || _flushTemp-- > 0)
|
||||||
|
{
|
||||||
|
// Buffers that flush should ideally be mapped in host address space for easy copies.
|
||||||
|
// If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages).
|
||||||
|
// If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached.
|
||||||
|
DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
|
||||||
|
|
||||||
|
// It's harder for a buffer that is flushed to revert to another type of mapping.
|
||||||
|
if (_flushCount > 0)
|
||||||
|
{
|
||||||
|
_flushTemp = 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_writeCount >= WriteCountThreshold)
|
||||||
|
{
|
||||||
|
// Buffers that are written often should ideally be in the device local heap. (Storage buffers)
|
||||||
|
DesiredType = BufferAllocationType.DeviceLocal;
|
||||||
|
}
|
||||||
|
else if (_setCount > SetCountThreshold)
|
||||||
|
{
|
||||||
|
// Buffers that have their data set often should ideally be host mapped. (Constant buffers)
|
||||||
|
DesiredType = BufferAllocationType.HostMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushCount = 0;
|
||||||
|
_writeCount = 0;
|
||||||
|
_setCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_swapQueued && DesiredType != _currentType)
|
||||||
|
{
|
||||||
|
_swapQueued = true;
|
||||||
|
|
||||||
|
_gd.PipelineInternal.AddBackingSwap(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView)
|
||||||
{
|
{
|
||||||
var bufferViewCreateInfo = new BufferViewCreateInfo()
|
var bufferViewCreateInfo = new BufferViewCreateInfo()
|
||||||
{
|
{
|
||||||
@@ -62,9 +232,19 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||||
|
|
||||||
|
(_swapActions ??= new List<Action>()).Add(invalidateView);
|
||||||
|
|
||||||
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
|
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InheritMetrics(BufferHolder other)
|
||||||
|
{
|
||||||
|
_setCount = other._setCount;
|
||||||
|
_writeCount = other._writeCount;
|
||||||
|
_flushCount = other._flushCount;
|
||||||
|
_flushTemp = other._flushTemp;
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
|
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
|
||||||
{
|
{
|
||||||
// If the last access is write, we always need a barrier to be sure we will read or modify
|
// If the last access is write, we always need a barrier to be sure we will read or modify
|
||||||
@@ -104,12 +284,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false)
|
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false, bool isSSBO = false)
|
||||||
{
|
{
|
||||||
if (isWrite)
|
if (isWrite)
|
||||||
{
|
{
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
SignalWrite(0, Size);
|
SignalWrite(0, Size);
|
||||||
}
|
}
|
||||||
|
else if (isSSBO)
|
||||||
|
{
|
||||||
|
// Always consider SSBO access for swapping to device local memory.
|
||||||
|
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
|
ConsiderBackingSwap();
|
||||||
|
}
|
||||||
|
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
@@ -118,6 +308,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (isWrite)
|
if (isWrite)
|
||||||
{
|
{
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
SignalWrite(offset, size);
|
SignalWrite(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +318,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void SignalWrite(int offset, int size)
|
public void SignalWrite(int offset, int size)
|
||||||
{
|
{
|
||||||
|
ConsiderBackingSwap();
|
||||||
|
|
||||||
if (offset == 0 && size == Size)
|
if (offset == 0 && size == Size)
|
||||||
{
|
{
|
||||||
_cachedConvertedBuffers.Clear();
|
_cachedConvertedBuffers.Clear();
|
||||||
@@ -147,11 +341,76 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _map;
|
return _map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
|
private void ClearFlushFence()
|
||||||
{
|
{
|
||||||
|
// Asusmes _flushLock is held as writer.
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
if (_flushWaiting == 0)
|
||||||
|
{
|
||||||
|
_flushFence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushFence = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WaitForFlushFence()
|
||||||
|
{
|
||||||
|
// Assumes the _flushLock is held as reader, returns in same state.
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
// If storage has changed, make sure the fence has been reached so that the data is in place.
|
||||||
|
|
||||||
|
var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
var fence = _flushFence;
|
||||||
|
Interlocked.Increment(ref _flushWaiting);
|
||||||
|
|
||||||
|
// Don't wait in the lock.
|
||||||
|
|
||||||
|
var restoreCookie = _flushLock.ReleaseLock();
|
||||||
|
|
||||||
|
fence.Wait();
|
||||||
|
|
||||||
|
_flushLock.RestoreLock(ref restoreCookie);
|
||||||
|
|
||||||
|
if (Interlocked.Decrement(ref _flushWaiting) == 0)
|
||||||
|
{
|
||||||
|
fence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushFence = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushLock.DowngradeFromWriterLock(ref cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe PinnedSpan<byte> GetData(int offset, int size)
|
||||||
|
{
|
||||||
|
_flushLock.AcquireReaderLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
WaitForFlushFence();
|
||||||
|
|
||||||
|
_flushCount++;
|
||||||
|
|
||||||
|
Span<byte> result;
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
if (_map != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
return GetDataStorage(offset, size);
|
result = GetDataStorage(offset, size);
|
||||||
|
|
||||||
|
// Need to be careful here, the buffer can't be unmapped while the data is being used.
|
||||||
|
_buffer.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_flushLock.ReleaseReaderLock();
|
||||||
|
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -161,12 +420,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
|
result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_flushLock.ReleaseReaderLock();
|
||||||
|
|
||||||
|
// Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses.
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +454,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setCount++;
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
if (_map != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
||||||
@@ -268,6 +534,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
|
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
|
||||||
|
|
||||||
|
_writeCount--;
|
||||||
|
|
||||||
InsertBufferBarrier(
|
InsertBufferBarrier(
|
||||||
_gd,
|
_gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
@@ -502,11 +770,19 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
||||||
|
|
||||||
_buffer.Dispose();
|
_buffer.Dispose();
|
||||||
_allocationAuto.Dispose();
|
_allocationAuto.Dispose();
|
||||||
_cachedConvertedBuffers.Dispose();
|
_cachedConvertedBuffers.Dispose();
|
||||||
|
|
||||||
|
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
ClearFlushFence();
|
||||||
|
|
||||||
|
_flushLock.ReleaseWriterLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -16,17 +17,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
// Some drivers don't expose a "HostCached" memory type,
|
// Some drivers don't expose a "HostCached" memory type,
|
||||||
// so we need those alternative flags for the allocation to succeed there.
|
// so we need those alternative flags for the allocation to succeed there.
|
||||||
private const MemoryPropertyFlags DefaultBufferMemoryAltFlags =
|
private const MemoryPropertyFlags DefaultBufferMemoryNoCacheFlags =
|
||||||
MemoryPropertyFlags.HostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.HostCoherentBit;
|
MemoryPropertyFlags.HostCoherentBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
||||||
MemoryPropertyFlags.DeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags DeviceLocalMappedBufferMemoryFlags =
|
||||||
|
MemoryPropertyFlags.DeviceLocalBit |
|
||||||
MemoryPropertyFlags.HostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.HostCoherentBit |
|
MemoryPropertyFlags.HostCoherentBit;
|
||||||
MemoryPropertyFlags.DeviceLocalBit;
|
|
||||||
|
|
||||||
private const BufferUsageFlags DefaultBufferUsageFlags =
|
private const BufferUsageFlags DefaultBufferUsageFlags =
|
||||||
BufferUsageFlags.TransferSrcBit |
|
BufferUsageFlags.TransferSrcBit |
|
||||||
@@ -54,14 +55,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
StagingBuffer = new StagingBuffer(gd, this);
|
StagingBuffer = new StagingBuffer(gd, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal)
|
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
return CreateWithHandle(gd, size, deviceLocal, out _);
|
return CreateWithHandle(gd, size, out _, baseType, storageHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder)
|
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
holder = Create(gd, size, deviceLocal: deviceLocal);
|
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
|
||||||
if (holder == null)
|
if (holder == null)
|
||||||
{
|
{
|
||||||
return BufferHandle.Null;
|
return BufferHandle.Null;
|
||||||
@@ -74,7 +75,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe BufferHolder Create(VulkanRenderer gd, int size, bool forConditionalRendering = false, bool deviceLocal = false)
|
public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
int size,
|
||||||
|
BufferAllocationType type,
|
||||||
|
bool forConditionalRendering = false,
|
||||||
|
BufferAllocationType fallbackType = BufferAllocationType.Auto)
|
||||||
{
|
{
|
||||||
var usage = DefaultBufferUsageFlags;
|
var usage = DefaultBufferUsageFlags;
|
||||||
|
|
||||||
@@ -98,48 +104,106 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
||||||
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||||
|
|
||||||
MemoryPropertyFlags allocateFlags;
|
MemoryAllocation allocation;
|
||||||
MemoryPropertyFlags allocateFlagsAlt;
|
|
||||||
|
|
||||||
if (deviceLocal)
|
do
|
||||||
{
|
{
|
||||||
allocateFlags = DeviceLocalBufferMemoryFlags;
|
var allocateFlags = type switch
|
||||||
allocateFlagsAlt = DeviceLocalBufferMemoryFlags;
|
{
|
||||||
}
|
BufferAllocationType.HostMappedNoCache => DefaultBufferMemoryNoCacheFlags,
|
||||||
else
|
BufferAllocationType.HostMapped => DefaultBufferMemoryFlags,
|
||||||
{
|
BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags,
|
||||||
allocateFlags = DefaultBufferMemoryFlags;
|
BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags,
|
||||||
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
|
_ => DefaultBufferMemoryFlags
|
||||||
}
|
};
|
||||||
|
|
||||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
|
// If an allocation with this memory type fails, fall back to the previous one.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, true);
|
||||||
|
}
|
||||||
|
catch (VulkanException)
|
||||||
|
{
|
||||||
|
allocation = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (allocation.Memory.Handle == 0 && (--type != fallbackType));
|
||||||
|
|
||||||
if (allocation.Memory.Handle == 0UL)
|
if (allocation.Memory.Handle == 0UL)
|
||||||
{
|
{
|
||||||
gd.Api.DestroyBuffer(_device, buffer, null);
|
gd.Api.DestroyBuffer(_device, buffer, null);
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
|
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
|
||||||
|
|
||||||
return new BufferHolder(gd, _device, buffer, allocation, size);
|
return (buffer, allocation, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size)
|
public unsafe BufferHolder Create(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
int size,
|
||||||
|
bool forConditionalRendering = false,
|
||||||
|
BufferAllocationType baseType = BufferAllocationType.HostMapped,
|
||||||
|
BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
BufferAllocationType type = baseType;
|
||||||
|
BufferHolder storageHintHolder = null;
|
||||||
|
|
||||||
|
if (baseType == BufferAllocationType.Auto)
|
||||||
{
|
{
|
||||||
return holder.CreateView(format, offset, size);
|
if (gd.IsSharedMemory)
|
||||||
|
{
|
||||||
|
baseType = BufferAllocationType.HostMapped;
|
||||||
|
type = baseType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageHint != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
if (TryGetBuffer(storageHint, out storageHintHolder))
|
||||||
|
{
|
||||||
|
type = storageHintHolder.DesiredType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
|
||||||
|
CreateBacking(gd, size, type, forConditionalRendering);
|
||||||
|
|
||||||
|
if (buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType);
|
||||||
|
|
||||||
|
if (storageHintHolder != null)
|
||||||
|
{
|
||||||
|
holder.InheritMetrics(storageHintHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite)
|
public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size, Action invalidateView)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
if (TryGetBuffer(handle, out var holder))
|
||||||
{
|
{
|
||||||
return holder.GetBuffer(commandBuffer, isWrite);
|
return holder.CreateView(format, offset, size, invalidateView);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, bool isSSBO = false)
|
||||||
|
{
|
||||||
|
if (TryGetBuffer(handle, out var holder))
|
||||||
|
{
|
||||||
|
return holder.GetBuffer(commandBuffer, isWrite, isSSBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -332,14 +396,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(BufferHandle handle, int offset, int size)
|
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
if (TryGetBuffer(handle, out var holder))
|
||||||
{
|
{
|
||||||
return holder.GetData(offset, size);
|
return holder.GetData(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReadOnlySpan<byte>.Empty;
|
return new PinnedSpan<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
||||||
|
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
readonly struct BufferState : IDisposable
|
struct BufferState : IDisposable
|
||||||
{
|
{
|
||||||
public static BufferState Null => new BufferState(null, 0, 0);
|
public static BufferState Null => new BufferState(null, 0, 0);
|
||||||
|
|
||||||
private readonly int _offset;
|
private readonly int _offset;
|
||||||
private readonly int _size;
|
private readonly int _size;
|
||||||
|
|
||||||
private readonly Auto<DisposableBuffer> _buffer;
|
private Auto<DisposableBuffer> _buffer;
|
||||||
|
|
||||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
|
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
@@ -29,6 +29,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_buffer?.DecrementReferenceCount();
|
_buffer?.DecrementReferenceCount();
|
||||||
|
@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings.
|
// If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings.
|
||||||
_dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, deviceLocal: true);
|
_dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, baseType: BufferAllocationType.DeviceLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dummyTexture = gd.CreateTextureView(new TextureCreateInfo(
|
_dummyTexture = gd.CreateTextureView(new TextureCreateInfo(
|
||||||
@@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var buffer = assignment.Range;
|
var buffer = assignment.Range;
|
||||||
int index = assignment.Binding;
|
int index = assignment.Binding;
|
||||||
|
|
||||||
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true);
|
||||||
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
||||||
|
|
||||||
DescriptorBufferInfo info = new DescriptorBufferInfo()
|
DescriptorBufferInfo info = new DescriptorBufferInfo()
|
||||||
@@ -640,6 +640,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Array.Clear(_storageSet);
|
Array.Clear(_storageSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
if (list[i] == from)
|
||||||
|
{
|
||||||
|
list[i] = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
SwapBuffer(_uniformBufferRefs, from, to);
|
||||||
|
SwapBuffer(_storageBufferRefs, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
@@ -156,11 +156,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
};
|
};
|
||||||
|
|
||||||
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
|
||||||
|
|
||||||
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
|
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
|
||||||
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false);
|
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float));
|
||||||
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
|
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
|
||||||
|
|
||||||
int threadGroupWorkRegionDim = 16;
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
@@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
|
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
|
|
||||||
|
@@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
|
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
|
@@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
@@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
||||||
|
|
||||||
@@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
@@ -802,7 +802,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
shaderParams[2] = size;
|
shaderParams[2] = size;
|
||||||
shaderParams[3] = srcOffset;
|
shaderParams[3] = srcOffset;
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
@@ -958,7 +958,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
shaderParams[0] = BitOperations.Log2((uint)ratio);
|
shaderParams[0] = BitOperations.Log2((uint)ratio);
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
@@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
@@ -1133,7 +1133,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
@@ -1407,7 +1407,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
|
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
|
||||||
|
|
||||||
var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer);
|
var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer);
|
||||||
var patternBufferAuto = patternBuffer.GetBuffer();
|
var patternBufferAuto = patternBuffer.GetBuffer();
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
|
||||||
|
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expand the repeating pattern to the number of requested primitives.
|
// Expand the repeating pattern to the number of requested primitives.
|
||||||
BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
|
BufferHandle newBuffer = _gd.BufferManager.CreateWithHandle(_gd, expectedSize * sizeof(int));
|
||||||
|
|
||||||
// Copy the old data to the new one.
|
// Copy the old data to the new one.
|
||||||
if (_repeatingBuffer != BufferHandle.Null)
|
if (_repeatingBuffer != BufferHandle.Null)
|
||||||
|
@@ -146,5 +146,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return _buffer == buffer;
|
return _buffer == buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,32 +28,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
MemoryRequirements requirements,
|
MemoryRequirements requirements,
|
||||||
MemoryPropertyFlags flags = 0)
|
MemoryPropertyFlags flags = 0,
|
||||||
|
bool isBuffer = false)
|
||||||
{
|
{
|
||||||
return AllocateDeviceMemory(requirements, flags, flags);
|
int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags);
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
|
||||||
MemoryRequirements requirements,
|
|
||||||
MemoryPropertyFlags flags,
|
|
||||||
MemoryPropertyFlags alternativeFlags)
|
|
||||||
{
|
|
||||||
int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
|
|
||||||
if (memoryTypeIndex < 0)
|
if (memoryTypeIndex < 0)
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
|
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
|
||||||
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map);
|
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map)
|
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blockLists.Count; i++)
|
for (int i = 0; i < _blockLists.Count; i++)
|
||||||
{
|
{
|
||||||
var bl = _blockLists[i];
|
var bl = _blockLists[i];
|
||||||
if (bl.MemoryTypeIndex == memoryTypeIndex)
|
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||||
{
|
{
|
||||||
lock (bl)
|
lock (bl)
|
||||||
{
|
{
|
||||||
@@ -62,18 +55,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment);
|
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||||
_blockLists.Add(newBl);
|
_blockLists.Add(newBl);
|
||||||
return newBl.Allocate(size, alignment, map);
|
return newBl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int FindSuitableMemoryTypeIndex(
|
private int FindSuitableMemoryTypeIndex(
|
||||||
uint memoryTypeBits,
|
uint memoryTypeBits,
|
||||||
MemoryPropertyFlags flags,
|
MemoryPropertyFlags flags)
|
||||||
MemoryPropertyFlags alternativeFlags)
|
|
||||||
{
|
{
|
||||||
int bestCandidateIndex = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
||||||
{
|
{
|
||||||
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
||||||
@@ -84,14 +74,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
else if (type.PropertyFlags.HasFlag(alternativeFlags))
|
|
||||||
{
|
|
||||||
bestCandidateIndex = i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestCandidateIndex;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice)
|
||||||
|
{
|
||||||
|
// The device is regarded as having shared memory if all heaps have the device local bit.
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
|
||||||
|
|
||||||
|
for (int i = 0; i < properties.MemoryHeapCount; i++)
|
||||||
|
{
|
||||||
|
if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@@ -162,15 +162,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
|
|
||||||
public int MemoryTypeIndex { get; }
|
public int MemoryTypeIndex { get; }
|
||||||
|
public bool ForBuffer { get; }
|
||||||
|
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
|
||||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment)
|
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||||
{
|
{
|
||||||
_blocks = new List<Block>();
|
_blocks = new List<Block>();
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
MemoryTypeIndex = memoryTypeIndex;
|
MemoryTypeIndex = memoryTypeIndex;
|
||||||
|
ForBuffer = forBuffer;
|
||||||
_blockAlignment = blockAlignment;
|
_blockAlignment = blockAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1297,6 +1297,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
_indexBuffer.Swap(from, to);
|
||||||
|
|
||||||
|
for (int i = 0; i < _vertexBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
_vertexBuffers[i].Swap(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _transformFeedbackBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
_transformFeedbackBuffers[i].Swap(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
_descriptorSetUpdater.SwapBuffer(from, to);
|
||||||
|
|
||||||
|
SignalCommandBufferChange();
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe void TextureBarrier()
|
public unsafe void TextureBarrier()
|
||||||
{
|
{
|
||||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
|
@@ -17,10 +17,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private ulong _byteWeight;
|
private ulong _byteWeight;
|
||||||
|
|
||||||
|
private List<BufferHolder> _backingSwaps;
|
||||||
|
|
||||||
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
||||||
{
|
{
|
||||||
_activeQueries = new List<(QueryPool, bool)>();
|
_activeQueries = new List<(QueryPool, bool)>();
|
||||||
_pendingQueryCopies = new();
|
_pendingQueryCopies = new();
|
||||||
|
_backingSwaps = new();
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
}
|
}
|
||||||
@@ -185,6 +188,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TryBackingSwaps()
|
||||||
|
{
|
||||||
|
CommandBufferScoped? cbs = null;
|
||||||
|
|
||||||
|
_backingSwaps.RemoveAll((holder) => holder.TryBackingSwap(ref cbs));
|
||||||
|
|
||||||
|
cbs?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddBackingSwap(BufferHolder holder)
|
||||||
|
{
|
||||||
|
_backingSwaps.Add(holder);
|
||||||
|
}
|
||||||
|
|
||||||
public void Restore()
|
public void Restore()
|
||||||
{
|
{
|
||||||
if (Pipeline != null)
|
if (Pipeline != null)
|
||||||
@@ -230,6 +247,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Gd.ResetCounterPool();
|
Gd.ResetCounterPool();
|
||||||
|
|
||||||
|
TryBackingSwaps();
|
||||||
|
|
||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,12 +57,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
return _gd.GetBufferData(_bufferHandle, _offset, _size);
|
return _gd.GetBufferData(_bufferHandle, _offset, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
return GetData();
|
return GetData();
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_bufferView == null)
|
if (_bufferView == null)
|
||||||
{
|
{
|
||||||
_bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size);
|
_bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _bufferView?.Get(cbs, _offset, _size).Value ?? default;
|
return _bufferView?.Get(cbs, _offset, _size).Value ?? default;
|
||||||
@@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return bufferView.Get(cbs, _offset, _size).Value;
|
return bufferView.Get(cbs, _offset, _size).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size);
|
bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl);
|
||||||
|
|
||||||
if (bufferView != null)
|
if (bufferView != null)
|
||||||
{
|
{
|
||||||
|
@@ -531,7 +531,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
BackgroundResource resources = _gd.BackgroundResources.Get();
|
BackgroundResource resources = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
@@ -539,15 +539,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer());
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetData(resources.GetPool(), resources.GetFlushBuffer());
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
BackgroundResource resources = _gd.BackgroundResources.Get();
|
BackgroundResource resources = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
@@ -555,11 +555,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -129,6 +129,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _buffer == buffer;
|
return _buffer == buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Only dispose if this buffer is not refetched on each bind.
|
// Only dispose if this buffer is not refetched on each bind.
|
||||||
|
@@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal bool IsAmdGcn { get; private set; }
|
internal bool IsAmdGcn { get; private set; }
|
||||||
internal bool IsMoltenVk { get; private set; }
|
internal bool IsMoltenVk { get; private set; }
|
||||||
internal bool IsTBDR { get; private set; }
|
internal bool IsTBDR { get; private set; }
|
||||||
|
internal bool IsSharedMemory { get; private set; }
|
||||||
public string GpuVendor { get; private set; }
|
public string GpuVendor { get; private set; }
|
||||||
public string GpuRenderer { get; private set; }
|
public string GpuRenderer { get; private set; }
|
||||||
public string GpuVersion { get; private set; }
|
public string GpuVersion { get; private set; }
|
||||||
@@ -313,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
portabilityFlags,
|
portabilityFlags,
|
||||||
vertexBufferAlignment);
|
vertexBufferAlignment);
|
||||||
|
|
||||||
|
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice);
|
||||||
|
|
||||||
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
||||||
|
|
||||||
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
@@ -373,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
return BufferManager.CreateWithHandle(this, size, false);
|
return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
|
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
|
||||||
@@ -439,7 +442,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_syncManager.RegisterFlush();
|
_syncManager.RegisterFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
return BufferManager.GetData(buffer, offset, size);
|
return BufferManager.GetData(buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem;
|
|||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Ncm;
|
using LibHac.Tools.Ncm;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Services.Ssl;
|
using Ryujinx.HLE.HOS.Services.Ssl;
|
||||||
@@ -637,12 +638,12 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
private Stream GetZipStream(ZipArchiveEntry entry)
|
private Stream GetZipStream(ZipArchiveEntry entry)
|
||||||
{
|
{
|
||||||
MemoryStream dest = new MemoryStream();
|
MemoryStream dest = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
Stream src = entry.Open();
|
using (Stream src = entry.Open())
|
||||||
|
{
|
||||||
src.CopyTo(dest);
|
src.CopyTo(dest);
|
||||||
src.Dispose();
|
}
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
|
|||||||
|
|
||||||
private byte[] BuildResponseOld(WebCommonReturnValue result)
|
private byte[] BuildResponseOld(WebCommonReturnValue result)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(result);
|
writer.WriteStruct(result);
|
||||||
@@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
|
|||||||
}
|
}
|
||||||
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
|
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(new WebArgHeader
|
writer.WriteStruct(new WebArgHeader
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||||
@@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
private byte[] BuildResponse(ControllerSupportResultInfo result)
|
private byte[] BuildResponse(ControllerSupportResultInfo result)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
|
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
|
||||||
@@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
private byte[] BuildResponse()
|
private byte[] BuildResponse()
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write((ulong)ResultCode.Success);
|
writer.Write((ulong)ResultCode.Success);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
{
|
{
|
||||||
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
|
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write((ulong)PlayerSelectResult.Success);
|
writer.Write((ulong)PlayerSelectResult.Success);
|
||||||
|
@@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
||||||
|
|
||||||
int[] defaultCapabilities = new int[]
|
uint[] defaultCapabilities = new uint[]
|
||||||
{
|
{
|
||||||
0x030363F7,
|
0x030363F7,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
@@ -552,4 +552,4 @@ namespace Ryujinx.HLE.HOS
|
|||||||
IsPaused = pause;
|
IsPaused = pause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,3 +1,6 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
|
|
||||||
HasPId = (word & 1) != 0;
|
HasPId = (word & 1) != 0;
|
||||||
|
|
||||||
ToCopy = new int[(word >> 1) & 0xf];
|
|
||||||
ToMove = new int[(word >> 5) & 0xf];
|
|
||||||
|
|
||||||
PId = HasPId ? reader.ReadUInt64() : 0;
|
PId = HasPId ? reader.ReadUInt64() : 0;
|
||||||
|
|
||||||
for (int index = 0; index < ToCopy.Length; index++)
|
int toCopySize = (word >> 1) & 0xf;
|
||||||
|
int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
|
||||||
|
|
||||||
|
for (int index = 0; index < toCopy.Length; index++)
|
||||||
{
|
{
|
||||||
ToCopy[index] = reader.ReadInt32();
|
toCopy[index] = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < ToMove.Length; index++)
|
ToCopy = toCopy;
|
||||||
|
|
||||||
|
int toMoveSize = (word >> 5) & 0xf;
|
||||||
|
int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
|
||||||
|
|
||||||
|
for (int index = 0; index < toMove.Length; index++)
|
||||||
{
|
{
|
||||||
ToMove[index] = reader.ReadInt32();
|
toMove[index] = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToMove = toMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move)
|
public IpcHandleDesc(int[] copy, int[] move)
|
||||||
@@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
return new IpcHandleDesc(Array.Empty<int>(), handles);
|
return new IpcHandleDesc(Array.Empty<int>(), handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes()
|
public RecyclableMemoryStream GetStream()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word = HasPId ? 1 : 0;
|
||||||
|
|
||||||
|
word |= (ToCopy.Length & 0xf) << 1;
|
||||||
|
word |= (ToMove.Length & 0xf) << 5;
|
||||||
|
|
||||||
|
ms.Write(word);
|
||||||
|
|
||||||
|
if (HasPId)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
ms.Write(PId);
|
||||||
|
|
||||||
int word = HasPId ? 1 : 0;
|
|
||||||
|
|
||||||
word |= (ToCopy.Length & 0xf) << 1;
|
|
||||||
word |= (ToMove.Length & 0xf) << 5;
|
|
||||||
|
|
||||||
writer.Write(word);
|
|
||||||
|
|
||||||
if (HasPId)
|
|
||||||
{
|
|
||||||
writer.Write(PId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToCopy)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToMove)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(ToCopy);
|
||||||
|
ms.Write(ToMove);
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,8 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
ObjectIds = new List<int>();
|
ObjectIds = new List<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcMessage(byte[] data, long cmdPtr) : this()
|
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream(data))
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
|
||||||
{
|
{
|
||||||
BinaryReader reader = new BinaryReader(ms);
|
BinaryReader reader = new BinaryReader(ms);
|
||||||
|
|
||||||
@@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
|
|
||||||
for (int index = 0; index < recvListCount; index++)
|
for (int index = 0; index < recvListCount; index++)
|
||||||
{
|
{
|
||||||
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
|
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
|
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word0;
|
||||||
|
int word1;
|
||||||
|
|
||||||
|
word0 = (int)Type;
|
||||||
|
word0 |= (PtrBuff.Count & 0xf) << 16;
|
||||||
|
word0 |= (SendBuff.Count & 0xf) << 20;
|
||||||
|
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||||
|
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||||
|
|
||||||
|
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
|
||||||
|
|
||||||
|
int dataLength = RawData?.Length ?? 0;
|
||||||
|
|
||||||
|
dataLength = (dataLength + 3) & ~3;
|
||||||
|
|
||||||
|
int rawLength = dataLength;
|
||||||
|
|
||||||
|
int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
|
||||||
|
|
||||||
|
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
||||||
|
// padding before Raw Data too, we need to subtract the size of this padding.
|
||||||
|
// This is the weirdest padding I've seen so far...
|
||||||
|
int pad1 = 0x10 - pad0;
|
||||||
|
|
||||||
|
dataLength = (dataLength + pad0 + pad1) / 4;
|
||||||
|
|
||||||
|
word1 = (dataLength & 0x3ff) | (2 << 10);
|
||||||
|
|
||||||
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
word1 |= 1 << 31;
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (PtrBuff.Count & 0xf) << 16;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = Array.Empty<byte>();
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + 3) & ~3;
|
|
||||||
|
|
||||||
int rawLength = dataLength;
|
|
||||||
|
|
||||||
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
|
|
||||||
|
|
||||||
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
|
||||||
// padding before Raw Data too, we need to subtract the size of this padding.
|
|
||||||
// This is the weirdest padding I've seen so far...
|
|
||||||
int pad1 = 0x10 - pad0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + pad0 + pad1) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff) | (2 << 10);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
for (int index = 0; index < PtrBuff.Count; index++)
|
|
||||||
{
|
|
||||||
writer.Write(PtrBuff[index].GetWord0());
|
|
||||||
writer.Write(PtrBuff[index].GetWord1());
|
|
||||||
}
|
|
||||||
|
|
||||||
ms.Seek(pad0, SeekOrigin.Current);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(new byte[pad1]);
|
|
||||||
writer.Write(recvListAddr);
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(word0);
|
||||||
|
ms.Write(word1);
|
||||||
|
|
||||||
|
if (handleDataStream != null)
|
||||||
|
{
|
||||||
|
ms.Write(handleDataStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
|
||||||
|
{
|
||||||
|
ms.Write(ptrBuffDesc.GetWord0());
|
||||||
|
ms.Write(ptrBuffDesc.GetWord1());
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.WriteByte(0, pad0);
|
||||||
|
|
||||||
|
if (RawData != null)
|
||||||
|
{
|
||||||
|
ms.Write(RawData);
|
||||||
|
ms.WriteByte(0, rawLength - RawData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.WriteByte(0, pad1);
|
||||||
|
|
||||||
|
ms.Write(recvListAddr);
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytesTipc()
|
public RecyclableMemoryStream GetStreamTipc()
|
||||||
{
|
{
|
||||||
Debug.Assert(PtrBuff.Count == 0);
|
Debug.Assert(PtrBuff.Count == 0);
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word0;
|
||||||
|
int word1;
|
||||||
|
|
||||||
|
word0 = (int)Type;
|
||||||
|
word0 |= (SendBuff.Count & 0xf) << 20;
|
||||||
|
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||||
|
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||||
|
|
||||||
|
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
|
||||||
|
|
||||||
|
int dataLength = RawData?.Length ?? 0;
|
||||||
|
|
||||||
|
dataLength = ((dataLength + 3) & ~3) / 4;
|
||||||
|
|
||||||
|
word1 = (dataLength & 0x3ff);
|
||||||
|
|
||||||
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
word1 |= 1 << 31;
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = Array.Empty<byte>();
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = ((dataLength + 3) & ~3) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(word0);
|
||||||
|
ms.Write(word1);
|
||||||
|
|
||||||
|
if (handleDataStream != null)
|
||||||
|
{
|
||||||
|
ms.Write(handleDataStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RawData != null)
|
||||||
|
{
|
||||||
|
ms.Write(RawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetPadSize16(long position)
|
private long GetPadSize16(long position)
|
||||||
|
@@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
Size = size;
|
Size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcRecvListBuffDesc(BinaryReader reader)
|
public IpcRecvListBuffDesc(ulong packedValue)
|
||||||
{
|
{
|
||||||
ulong value = reader.ReadUInt64();
|
Position = packedValue & 0xffffffffffff;
|
||||||
|
|
||||||
Position = value & 0xffffffffffff;
|
Size = (ushort)(packedValue >> 48);
|
||||||
|
|
||||||
Size = (ushort)(value >> 48);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
|
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
|
||||||
{
|
{
|
||||||
Object = schedulerObj;
|
Object = schedulerObj;
|
||||||
TimePoint = timePoint;
|
TimePoint = timePoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
private bool _keepRunning;
|
private bool _keepRunning;
|
||||||
private long _enforceWakeupFromSpinWait;
|
private long _enforceWakeupFromSpinWait;
|
||||||
|
|
||||||
|
private const long NanosecondsPerSecond = 1000000000L;
|
||||||
|
private const long NanosecondsPerMillisecond = 1000000L;
|
||||||
|
|
||||||
public KTimeManager(KernelContext context)
|
public KTimeManager(KernelContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
@@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
{
|
{
|
||||||
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
|
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
|
||||||
|
|
||||||
if (timeout < 1000000)
|
if (timeout < NanosecondsPerMillisecond)
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
|
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
private WaitingObject GetNextWaitingObject()
|
private WaitingObject GetNextWaitingObject()
|
||||||
{
|
{
|
||||||
WaitingObject selected = null;
|
WaitingObject selected = null;
|
||||||
|
|
||||||
long lowestTimePoint = long.MaxValue;
|
long lowestTimePoint = long.MaxValue;
|
||||||
|
|
||||||
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
|
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
|
||||||
@@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public static long ConvertNanosecondsToMilliseconds(long time)
|
public static long ConvertNanosecondsToMilliseconds(long time)
|
||||||
{
|
{
|
||||||
time /= 1000000;
|
time /= NanosecondsPerMillisecond;
|
||||||
|
|
||||||
if ((ulong)time > int.MaxValue)
|
if ((ulong)time > int.MaxValue)
|
||||||
{
|
{
|
||||||
@@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public static long ConvertMillisecondsToNanoseconds(long time)
|
public static long ConvertMillisecondsToNanoseconds(long time)
|
||||||
{
|
{
|
||||||
return time * 1000000;
|
return time * NanosecondsPerMillisecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertNanosecondsToHostTicks(long ns)
|
public static long ConvertNanosecondsToHostTicks(long ns)
|
||||||
{
|
{
|
||||||
long nsDiv = ns / 1000000000;
|
long nsDiv = ns / NanosecondsPerSecond;
|
||||||
long nsMod = ns % 1000000000;
|
long nsMod = ns % NanosecondsPerSecond;
|
||||||
long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
|
long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
|
||||||
long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
|
long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
|
||||||
|
|
||||||
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
|
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
|
||||||
return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
|
return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertGuestTicksToNanoseconds(long ticks)
|
public static long ConvertGuestTicksToNanoseconds(long ticks)
|
||||||
|
@@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
public const int InitialKipId = 1;
|
public const int InitialKipId = 1;
|
||||||
public const int InitialProcessId = 0x51;
|
public const int InitialProcessId = 0x51;
|
||||||
|
|
||||||
|
public const int SupervisorCallCount = 0xC0;
|
||||||
|
|
||||||
public const int MemoryBlockAllocatorSize = 0x2710;
|
public const int MemoryBlockAllocatorSize = 0x2710;
|
||||||
|
|
||||||
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||||
@@ -15,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
|
|
||||||
public const ulong CounterFrequency = 19200000;
|
public const ulong CounterFrequency = 19200000;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
public static Result StartInitialProcess(
|
public static Result StartInitialProcess(
|
||||||
KernelContext context,
|
KernelContext context,
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
int mainThreadPriority,
|
int mainThreadPriority,
|
||||||
ThreadStart customThreadStart)
|
ThreadStart customThreadStart)
|
||||||
{
|
{
|
||||||
|
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
{
|
||||||
|
static class CapabilityExtensions
|
||||||
|
{
|
||||||
|
public static CapabilityType GetCapabilityType(this uint cap)
|
||||||
|
{
|
||||||
|
return (CapabilityType)(((cap + 1) & ~cap) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint GetFlag(this CapabilityType type)
|
||||||
|
{
|
||||||
|
return (uint)type + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint GetId(this CapabilityType type)
|
||||||
|
{
|
||||||
|
return (uint)BitOperations.TrailingZeroCount(type.GetFlag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
{
|
||||||
|
enum CapabilityType : uint
|
||||||
|
{
|
||||||
|
CorePriority = (1u << 3) - 1,
|
||||||
|
SyscallMask = (1u << 4) - 1,
|
||||||
|
MapRange = (1u << 6) - 1,
|
||||||
|
MapIoPage = (1u << 7) - 1,
|
||||||
|
MapRegion = (1u << 10) - 1,
|
||||||
|
InterruptPair = (1u << 11) - 1,
|
||||||
|
ProgramType = (1u << 13) - 1,
|
||||||
|
KernelVersion = (1u << 14) - 1,
|
||||||
|
HandleTable = (1u << 15) - 1,
|
||||||
|
DebugFlags = (1u << 16) - 1,
|
||||||
|
|
||||||
|
Invalid = 0u,
|
||||||
|
Padding = ~0u
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
private int _activeSlotsCount;
|
private int _activeSlotsCount;
|
||||||
|
|
||||||
private int _size;
|
private uint _size;
|
||||||
|
|
||||||
private ushort _idCounter;
|
private ushort _idCounter;
|
||||||
|
|
||||||
@@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(int size)
|
public Result Initialize(uint size)
|
||||||
{
|
{
|
||||||
if ((uint)size > 1024)
|
if (size > 1024)
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
@@ -16,11 +16,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class KProcess : KSynchronizationObject
|
class KProcess : KSynchronizationObject
|
||||||
{
|
{
|
||||||
public const int KernelVersionMajor = 10;
|
public const uint KernelVersionMajor = 10;
|
||||||
public const int KernelVersionMinor = 4;
|
public const uint KernelVersionMinor = 4;
|
||||||
public const int KernelVersionRevision = 0;
|
public const uint KernelVersionRevision = 0;
|
||||||
|
|
||||||
public const int KernelVersionPacked =
|
public const uint KernelVersionPacked =
|
||||||
(KernelVersionMajor << 19) |
|
(KernelVersionMajor << 19) |
|
||||||
(KernelVersionMinor << 15) |
|
(KernelVersionMinor << 15) |
|
||||||
(KernelVersionRevision << 0);
|
(KernelVersionRevision << 0);
|
||||||
@@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public Result InitializeKip(
|
public Result InitializeKip(
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
KPageList pageList,
|
KPageList pageList,
|
||||||
KResourceLimit resourceLimit,
|
KResourceLimit resourceLimit,
|
||||||
MemoryRegion memRegion,
|
MemoryRegion memRegion,
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public Result Initialize(
|
public Result Initialize(
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
KResourceLimit resourceLimit,
|
KResourceLimit resourceLimit,
|
||||||
MemoryRegion memRegion,
|
MemoryRegion memRegion,
|
||||||
IProcessContextFactory contextFactory,
|
IProcessContextFactory contextFactory,
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
@@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class KProcessCapabilities
|
class KProcessCapabilities
|
||||||
{
|
{
|
||||||
public byte[] SvcAccessMask { get; private set; }
|
public byte[] SvcAccessMask { get; }
|
||||||
public byte[] IrqAccessMask { get; private set; }
|
public byte[] IrqAccessMask { get; }
|
||||||
|
|
||||||
public ulong AllowedCpuCoresMask { get; private set; }
|
public ulong AllowedCpuCoresMask { get; private set; }
|
||||||
public ulong AllowedThreadPriosMask { get; private set; }
|
public ulong AllowedThreadPriosMask { get; private set; }
|
||||||
|
|
||||||
public int DebuggingFlags { get; private set; }
|
public uint DebuggingFlags { get; private set; }
|
||||||
public int HandleTableSize { get; private set; }
|
public uint HandleTableSize { get; private set; }
|
||||||
public int KernelReleaseVersion { get; private set; }
|
public uint KernelReleaseVersion { get; private set; }
|
||||||
public int ApplicationType { get; private set; }
|
public uint ApplicationType { get; private set; }
|
||||||
|
|
||||||
public KProcessCapabilities()
|
public KProcessCapabilities()
|
||||||
{
|
{
|
||||||
SvcAccessMask = new byte[0x10];
|
// length / number of bits of the underlying type
|
||||||
|
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
|
||||||
IrqAccessMask = new byte[0x80];
|
IrqAccessMask = new byte[0x80];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result InitializeForKernel(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
AllowedCpuCoresMask = 0xf;
|
AllowedCpuCoresMask = 0xf;
|
||||||
AllowedThreadPriosMask = ulong.MaxValue;
|
AllowedThreadPriosMask = ulong.MaxValue;
|
||||||
DebuggingFlags &= ~3;
|
DebuggingFlags &= ~3u;
|
||||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||||
|
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result Parse(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
int mask0 = 0;
|
int mask0 = 0;
|
||||||
int mask1 = 0;
|
int mask1 = 0;
|
||||||
|
|
||||||
for (int index = 0; index < capabilities.Length; index++)
|
for (int index = 0; index < capabilities.Length; index++)
|
||||||
{
|
{
|
||||||
int cap = capabilities[index];
|
uint cap = capabilities[index];
|
||||||
|
|
||||||
if (((cap + 1) & ~cap) != 0x40)
|
if (cap.GetCapabilityType() != CapabilityType.MapRange)
|
||||||
{
|
{
|
||||||
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
int prevCap = cap;
|
uint prevCap = cap;
|
||||||
|
|
||||||
cap = capabilities[++index];
|
cap = capabilities[++index];
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
long address = ((long)(uint)prevCap << 5) & 0xffffff000;
|
long address = ((long)prevCap << 5) & 0xffffff000;
|
||||||
long size = ((long)(uint)cap << 5) & 0xfffff000;
|
long size = ((long)cap << 5) & 0xfffff000;
|
||||||
|
|
||||||
if (((ulong)(address + size - 1) >> 36) != 0)
|
if (((ulong)(address + size - 1) >> 36) != 0)
|
||||||
{
|
{
|
||||||
@@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
|
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
int code = (cap + 1) & ~cap;
|
CapabilityType code = cap.GetCapabilityType();
|
||||||
|
|
||||||
if (code == 1)
|
if (code == CapabilityType.Invalid)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCapability;
|
return KernelResult.InvalidCapability;
|
||||||
}
|
}
|
||||||
else if (code == 0)
|
else if (code == CapabilityType.Padding)
|
||||||
{
|
{
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1));
|
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1));
|
||||||
|
|
||||||
// Check if the property was already set.
|
// Check if the property was already set.
|
||||||
if (((mask0 & codeMask) & 0x1e008) != 0)
|
if (((mask0 & codeMask) & 0x1e008) != 0)
|
||||||
@@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 8:
|
case CapabilityType.CorePriority:
|
||||||
{
|
{
|
||||||
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCapability;
|
return KernelResult.InvalidCapability;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lowestCpuCore = (cap >> 16) & 0xff;
|
uint lowestCpuCore = (cap >> 16) & 0xff;
|
||||||
int highestCpuCore = (cap >> 24) & 0xff;
|
uint highestCpuCore = (cap >> 24) & 0xff;
|
||||||
|
|
||||||
if (lowestCpuCore > highestCpuCore)
|
if (lowestCpuCore > highestCpuCore)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
int highestThreadPrio = (cap >> 4) & 0x3f;
|
uint highestThreadPrio = (cap >> 4) & 0x3f;
|
||||||
int lowestThreadPrio = (cap >> 10) & 0x3f;
|
uint lowestThreadPrio = (cap >> 10) & 0x3f;
|
||||||
|
|
||||||
if (lowestThreadPrio > highestThreadPrio)
|
if (lowestThreadPrio > highestThreadPrio)
|
||||||
{
|
{
|
||||||
@@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x10:
|
case CapabilityType.SyscallMask:
|
||||||
{
|
{
|
||||||
int slot = (cap >> 29) & 7;
|
int slot = ((int)cap >> 29) & 7;
|
||||||
|
|
||||||
int svcSlotMask = 1 << slot;
|
int svcSlotMask = 1 << slot;
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
mask1 |= svcSlotMask;
|
mask1 |= svcSlotMask;
|
||||||
|
|
||||||
int svcMask = (cap >> 5) & 0xffffff;
|
uint svcMask = (cap >> 5) & 0xffffff;
|
||||||
|
|
||||||
int baseSvc = slot * 24;
|
int baseSvc = slot * 24;
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
int svcId = baseSvc + index;
|
int svcId = baseSvc + index;
|
||||||
|
|
||||||
if (svcId > 0x7f)
|
if (svcId >= KernelConstants.SupervisorCallCount)
|
||||||
{
|
{
|
||||||
return KernelResult.MaximumExceeded;
|
return KernelResult.MaximumExceeded;
|
||||||
}
|
}
|
||||||
@@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x80:
|
case CapabilityType.MapIoPage:
|
||||||
{
|
{
|
||||||
long address = ((long)(uint)cap << 4) & 0xffffff000;
|
long address = ((long)cap << 4) & 0xffffff000;
|
||||||
|
|
||||||
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
|
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x800:
|
case CapabilityType.MapRegion:
|
||||||
|
{
|
||||||
|
// TODO: Implement capabilities for MapRegion
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CapabilityType.InterruptPair:
|
||||||
{
|
{
|
||||||
// TODO: GIC distributor check.
|
// TODO: GIC distributor check.
|
||||||
int irq0 = (cap >> 12) & 0x3ff;
|
int irq0 = ((int)cap >> 12) & 0x3ff;
|
||||||
int irq1 = (cap >> 22) & 0x3ff;
|
int irq1 = ((int)cap >> 22) & 0x3ff;
|
||||||
|
|
||||||
if (irq0 != 0x3ff)
|
if (irq0 != 0x3ff)
|
||||||
{
|
{
|
||||||
@@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2000:
|
case CapabilityType.ProgramType:
|
||||||
{
|
{
|
||||||
int applicationType = cap >> 14;
|
uint applicationType = (cap >> 14);
|
||||||
|
|
||||||
if ((uint)applicationType > 7)
|
if (applicationType > 7)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
@@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x4000:
|
case CapabilityType.KernelVersion:
|
||||||
{
|
{
|
||||||
// Note: This check is bugged on kernel too, we are just replicating the bug here.
|
// Note: This check is bugged on kernel too, we are just replicating the bug here.
|
||||||
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
||||||
@@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x8000:
|
case CapabilityType.HandleTable:
|
||||||
{
|
{
|
||||||
int handleTableSize = cap >> 26;
|
uint handleTableSize = cap >> 26;
|
||||||
|
|
||||||
if ((uint)handleTableSize > 0x3ff)
|
if (handleTableSize > 0x3ff)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
@@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x10000:
|
case CapabilityType.DebugFlags:
|
||||||
{
|
{
|
||||||
int debuggingFlags = cap >> 19;
|
uint debuggingFlags = cap >> 19;
|
||||||
|
|
||||||
if ((uint)debuggingFlags > 3)
|
if (debuggingFlags > 3)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebuggingFlags &= ~3;
|
DebuggingFlags &= ~3u;
|
||||||
DebuggingFlags |= debuggingFlags;
|
DebuggingFlags |= debuggingFlags;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong GetMaskFromMinMax(int min, int max)
|
private static ulong GetMaskFromMinMax(uint min, uint max)
|
||||||
{
|
{
|
||||||
int range = max - min + 1;
|
uint range = max - min + 1;
|
||||||
|
|
||||||
if (range == 64)
|
if (range == 64)
|
||||||
{
|
{
|
||||||
return ulong.MaxValue;
|
return ulong.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong mask = (1UL << range) - 1;
|
ulong mask = (1UL << (int)range) - 1;
|
||||||
|
|
||||||
return mask << min;
|
return mask << (int)min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,8 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
enum ProcessCreationFlags
|
enum ProcessCreationFlags
|
||||||
{
|
{
|
||||||
Is64Bit = 1 << 0,
|
Is64Bit = 1 << 0,
|
||||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
public Result CreateProcess(
|
public Result CreateProcess(
|
||||||
out int handle,
|
out int handle,
|
||||||
ProcessCreationInfo info,
|
ProcessCreationInfo info,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
IProcessContextFactory contextFactory,
|
IProcessContextFactory contextFactory,
|
||||||
ThreadStart customThreadStart = null)
|
ThreadStart customThreadStart = null)
|
||||||
{
|
{
|
||||||
@@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
|
KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
|
||||||
|
|
||||||
for (int index = 0; index < handles.Length; index++)
|
for (int index = 0; index < handles.Length; index++)
|
||||||
{
|
{
|
||||||
@@ -3002,4 +3002,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return (address & 3) != 0;
|
return (address & 3) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
class KPriorityQueue
|
class KPriorityQueue
|
||||||
{
|
{
|
||||||
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
|
private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
|
||||||
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
|
private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
|
||||||
|
|
||||||
private long[] _scheduledPrioritiesPerCore;
|
private readonly long[] _scheduledPrioritiesPerCore;
|
||||||
private long[] _suggestedPrioritiesPerCore;
|
private readonly long[] _suggestedPrioritiesPerCore;
|
||||||
|
|
||||||
public KPriorityQueue()
|
public KPriorityQueue()
|
||||||
{
|
{
|
||||||
@@ -32,43 +32,134 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
|
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<KThread> SuggestedThreads(int core)
|
public readonly ref struct KThreadEnumerable
|
||||||
{
|
{
|
||||||
return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
|
readonly LinkedList<KThread>[][] _listPerPrioPerCore;
|
||||||
}
|
readonly long[] _prios;
|
||||||
|
readonly int _core;
|
||||||
|
|
||||||
public IEnumerable<KThread> ScheduledThreads(int core)
|
public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
||||||
{
|
|
||||||
return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
|
||||||
{
|
|
||||||
long prioMask = prios[core];
|
|
||||||
|
|
||||||
int prio = BitOperations.TrailingZeroCount(prioMask);
|
|
||||||
|
|
||||||
prioMask &= ~(1L << prio);
|
|
||||||
|
|
||||||
while (prio < KScheduler.PrioritiesCount)
|
|
||||||
{
|
{
|
||||||
LinkedList<KThread> list = listPerPrioPerCore[prio][core];
|
_listPerPrioPerCore = listPerPrioPerCore;
|
||||||
|
_prios = prios;
|
||||||
|
_core = core;
|
||||||
|
}
|
||||||
|
|
||||||
LinkedListNode<KThread> node = list.First;
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return new Enumerator(_listPerPrioPerCore, _prios, _core);
|
||||||
|
}
|
||||||
|
|
||||||
while (node != null)
|
public ref struct Enumerator
|
||||||
|
{
|
||||||
|
private readonly LinkedList<KThread>[][] _listPerPrioPerCore;
|
||||||
|
private readonly int _core;
|
||||||
|
private long _prioMask;
|
||||||
|
private int _prio;
|
||||||
|
private LinkedList<KThread> _list;
|
||||||
|
private LinkedListNode<KThread> _node;
|
||||||
|
|
||||||
|
public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
||||||
{
|
{
|
||||||
yield return node.Value;
|
_listPerPrioPerCore = listPerPrioPerCore;
|
||||||
|
_core = core;
|
||||||
node = node.Next;
|
_prioMask = prios[core];
|
||||||
|
_prio = BitOperations.TrailingZeroCount(_prioMask);
|
||||||
|
_prioMask &= ~(1L << _prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
prio = BitOperations.TrailingZeroCount(prioMask);
|
public KThread Current => _node?.Value;
|
||||||
|
|
||||||
prioMask &= ~(1L << prio);
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
_node = _node?.Next;
|
||||||
|
|
||||||
|
if (_node == null)
|
||||||
|
{
|
||||||
|
if (!MoveNextListAndFirstNode())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _node != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveNextListAndFirstNode()
|
||||||
|
{
|
||||||
|
if (_prio < KScheduler.PrioritiesCount)
|
||||||
|
{
|
||||||
|
_list = _listPerPrioPerCore[_prio][_core];
|
||||||
|
|
||||||
|
_node = _list.First;
|
||||||
|
|
||||||
|
_prio = BitOperations.TrailingZeroCount(_prioMask);
|
||||||
|
|
||||||
|
_prioMask &= ~(1L << _prio);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_list = null;
|
||||||
|
_node = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KThreadEnumerable ScheduledThreads(int core)
|
||||||
|
{
|
||||||
|
return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThreadEnumerable SuggestedThreads(int core)
|
||||||
|
{
|
||||||
|
return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsFirstOrDefault(int core)
|
||||||
|
{
|
||||||
|
return ScheduledThreadsElementAtOrDefault(core, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
|
||||||
|
{
|
||||||
|
int currentIndex = 0;
|
||||||
|
foreach (var scheduledThread in ScheduledThreads(core))
|
||||||
|
{
|
||||||
|
if (currentIndex == index)
|
||||||
|
{
|
||||||
|
return scheduledThread;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
|
||||||
|
{
|
||||||
|
foreach (var scheduledThread in ScheduledThreads(core))
|
||||||
|
{
|
||||||
|
if (scheduledThread.DynamicPriority == dynamicPriority)
|
||||||
|
{
|
||||||
|
return scheduledThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasScheduledThreads(int core)
|
||||||
|
{
|
||||||
|
return ScheduledThreadsFirstOrDefault(core) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public void TransferToCore(int prio, int dstCore, KThread thread)
|
public void TransferToCore(int prio, int dstCore, KThread thread)
|
||||||
{
|
{
|
||||||
int srcCore = thread.ActiveCore;
|
int srcCore = thread.ActiveCore;
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
|
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
|
||||||
|
|
||||||
|
private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private readonly int _coreId;
|
private readonly int _coreId;
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
for (int core = 0; core < CpuCoresCount; core++)
|
for (int core = 0; core < CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
|
KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
|
||||||
|
|
||||||
if (thread != null &&
|
if (thread != null &&
|
||||||
thread.Owner != null &&
|
thread.Owner != null &&
|
||||||
@@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
// If the core is not idle (there's already a thread running on it),
|
// If the core is not idle (there's already a thread running on it),
|
||||||
// then we don't need to attempt load balancing.
|
// then we don't need to attempt load balancing.
|
||||||
if (context.PriorityQueue.ScheduledThreads(core).Any())
|
if (context.PriorityQueue.HasScheduledThreads(core))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
Array.Fill(_srcCoresHighestPrioThreads, 0);
|
||||||
|
|
||||||
int srcCoresHighestPrioThreadsCount = 0;
|
int srcCoresHighestPrioThreadsCount = 0;
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
|
_srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not yet selected candidate found.
|
// Not yet selected candidate found.
|
||||||
@@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// (the first one that doesn't make the source core idle if moved).
|
// (the first one that doesn't make the source core idle if moved).
|
||||||
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
|
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
|
||||||
{
|
{
|
||||||
int srcCore = srcCoresHighestPrioThreads[index];
|
int srcCore = _srcCoresHighestPrioThreads[index];
|
||||||
|
|
||||||
KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
|
KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1);
|
||||||
|
|
||||||
if (src != null)
|
if (src != null)
|
||||||
{
|
{
|
||||||
@@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
|
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
|
||||||
{
|
{
|
||||||
IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
|
KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio);
|
||||||
|
|
||||||
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
|
|
||||||
KThread nextThread = null;
|
KThread nextThread = null;
|
||||||
|
|
||||||
// Yield priority queue.
|
// Yield priority queue.
|
||||||
@@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
|
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<KThread> SuitableCandidates()
|
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate)
|
||||||
{
|
{
|
||||||
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
||||||
{
|
{
|
||||||
int suggestedCore = suggested.ActiveCore;
|
int suggestedCore = suggested.ActiveCore;
|
||||||
if (suggestedCore >= 0)
|
if (suggestedCore >= 0)
|
||||||
{
|
{
|
||||||
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
|
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
|
||||||
|
|
||||||
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
|
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
|
||||||
{
|
{
|
||||||
@@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
nextThread == null ||
|
nextThread == null ||
|
||||||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
|
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
|
||||||
{
|
{
|
||||||
yield return suggested;
|
if (predicate(suggested))
|
||||||
|
{
|
||||||
|
return suggested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select candidate threads that could run on this core.
|
// Select candidate threads that could run on this core.
|
||||||
// Only take into account threads that are not yet selected.
|
// Only take into account threads that are not yet selected.
|
||||||
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
|
KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
// If the priority of the currently selected thread is lower or same as the preemption priority,
|
// If the priority of the currently selected thread is lower or same as the preemption priority,
|
||||||
// then try to migrate a thread with lower priority.
|
// then try to migrate a thread with lower priority.
|
||||||
KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
|
KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
|
||||||
|
|
||||||
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
|
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
|
||||||
{
|
{
|
||||||
dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
|
dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// Move current thread to the end of the queue.
|
// Move current thread to the end of the queue.
|
||||||
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
|
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
|
||||||
|
|
||||||
IEnumerable<KThread> SuitableCandidates()
|
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority)
|
||||||
{
|
{
|
||||||
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
||||||
{
|
{
|
||||||
@@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
|
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
|
||||||
suggested.DynamicPriority < nextThread.DynamicPriority)
|
suggested.DynamicPriority < nextThread.DynamicPriority)
|
||||||
{
|
{
|
||||||
yield return suggested;
|
if (suggested.DynamicPriority <= lessThanOrEqualPriority)
|
||||||
|
{
|
||||||
|
return suggested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
|
KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
|
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
|
||||||
|
|
||||||
if (!context.PriorityQueue.ScheduledThreads(core).Any())
|
if (!context.PriorityQueue.HasScheduledThreads(core))
|
||||||
{
|
{
|
||||||
KThread selectedThread = null;
|
KThread selectedThread = null;
|
||||||
|
|
||||||
@@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
|
KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
|
||||||
|
|
||||||
if (firstCandidate == suggested)
|
if (firstCandidate == suggested)
|
||||||
{
|
{
|
||||||
|
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length];
|
LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
|
||||||
|
|
||||||
for (int index = 0; index < syncObjs.Length; index++)
|
for (int index = 0; index < syncObjs.Length; index++)
|
||||||
{
|
{
|
||||||
|
@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
|
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
|
||||||
|
|
||||||
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
|
ulong codeAddress = codeBaseAddress + kip.TextOffset;
|
||||||
|
|
||||||
ProcessCreationFlags flags = 0;
|
ProcessCreationFlags flags = 0;
|
||||||
|
|
||||||
@@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
||||||
|
|
||||||
nsoBase[index] = codeStart + (ulong)codeSize;
|
nsoBase[index] = codeStart + codeSize;
|
||||||
|
|
||||||
codeSize += nsoSize;
|
codeSize += nsoSize;
|
||||||
|
|
||||||
if (arguments != null && argsSize == 0)
|
if (arguments != null && argsSize == 0)
|
||||||
{
|
{
|
||||||
argsStart = (ulong)codeSize;
|
argsStart = codeSize;
|
||||||
|
|
||||||
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
|
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
result = process.Initialize(
|
result = process.Initialize(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
MemoryMarshal.Cast<byte, int>(npdm.KernelCapabilityData).ToArray(),
|
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
|
||||||
resourceLimit,
|
resourceLimit,
|
||||||
memoryRegion,
|
memoryRegion,
|
||||||
processContextFactory);
|
processContextFactory);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
||||||
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
|||||||
public static byte[] MakeLaunchParams(UserProfile userProfile)
|
public static byte[] MakeLaunchParams(UserProfile userProfile)
|
||||||
{
|
{
|
||||||
// Size needs to be at least 0x88 bytes otherwise application errors.
|
// Size needs to be at least 0x88 bytes otherwise application errors.
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter writer = new BinaryWriter(ms);
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
@@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
|
|||||||
static uint KXor(uint data) => data ^ FontKey;
|
static uint KXor(uint data) => data ^ FontKey;
|
||||||
|
|
||||||
using (BinaryReader reader = new BinaryReader(bfttfStream))
|
using (BinaryReader reader = new BinaryReader(bfttfStream))
|
||||||
using (MemoryStream ttfStream = new MemoryStream())
|
using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter output = new BinaryWriter(ttfStream))
|
using (BinaryWriter output = new BinaryWriter(ttfStream))
|
||||||
{
|
{
|
||||||
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
|
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
@@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -19,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
// not large enough.
|
// not large enough.
|
||||||
private const int PointerBufferSize = 0x8000;
|
private const int PointerBufferSize = 0x8000;
|
||||||
|
|
||||||
private readonly static int[] DefaultCapabilities = new int[]
|
private readonly static uint[] DefaultCapabilities = new uint[]
|
||||||
{
|
{
|
||||||
0x030363F7,
|
0x030363F7,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
@@ -29,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
0x01007FFF
|
0x01007FFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly object _handleLock = new();
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private KProcess _selfProcess;
|
private KProcess _selfProcess;
|
||||||
|
|
||||||
@@ -37,14 +42,27 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
||||||
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
||||||
|
|
||||||
|
private readonly MemoryStream _requestDataStream;
|
||||||
|
private readonly BinaryReader _requestDataReader;
|
||||||
|
|
||||||
|
private readonly MemoryStream _responseDataStream;
|
||||||
|
private readonly BinaryWriter _responseDataWriter;
|
||||||
|
|
||||||
public ManualResetEvent InitDone { get; }
|
public ManualResetEvent InitDone { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public Func<IpcService> SmObjectFactory { get; }
|
public Func<IpcService> SmObjectFactory { get; }
|
||||||
|
|
||||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
||||||
{
|
{
|
||||||
InitDone = new ManualResetEvent(false);
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
|
_requestDataStream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
_requestDataReader = new BinaryReader(_requestDataStream);
|
||||||
|
|
||||||
|
_responseDataStream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
_responseDataWriter = new BinaryWriter(_responseDataStream);
|
||||||
|
|
||||||
|
InitDone = new ManualResetEvent(false);
|
||||||
Name = name;
|
Name = name;
|
||||||
SmObjectFactory = smObjectFactory;
|
SmObjectFactory = smObjectFactory;
|
||||||
|
|
||||||
@@ -61,7 +79,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
||||||
{
|
{
|
||||||
_portHandles.Add(serverPortHandle);
|
lock (_handleLock)
|
||||||
|
{
|
||||||
|
_portHandles.Add(serverPortHandle);
|
||||||
|
}
|
||||||
_ports.Add(serverPortHandle, objectFactory);
|
_ports.Add(serverPortHandle, objectFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +97,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
||||||
{
|
{
|
||||||
_sessionHandles.Add(serverSessionHandle);
|
lock (_handleLock)
|
||||||
|
{
|
||||||
|
_sessionHandles.Add(serverSessionHandle);
|
||||||
|
}
|
||||||
_sessions.Add(serverSessionHandle, obj);
|
_sessions.Add(serverSessionHandle, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,15 +134,23 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int[] portHandles = _portHandles.ToArray();
|
int handleCount;
|
||||||
int[] sessionHandles = _sessionHandles.ToArray();
|
int portHandleCount;
|
||||||
int[] handles = new int[portHandles.Length + sessionHandles.Length];
|
int[] handles;
|
||||||
|
|
||||||
portHandles.CopyTo(handles, 0);
|
lock (_handleLock)
|
||||||
sessionHandles.CopyTo(handles, portHandles.Length);
|
{
|
||||||
|
portHandleCount = _portHandles.Count;
|
||||||
|
handleCount = portHandleCount + _sessionHandles.Count;
|
||||||
|
|
||||||
|
handles = ArrayPool<int>.Shared.Rent(handleCount);
|
||||||
|
|
||||||
|
_portHandles.CopyTo(handles, 0);
|
||||||
|
_sessionHandles.CopyTo(handles, portHandleCount);
|
||||||
|
}
|
||||||
|
|
||||||
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
||||||
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
|
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
|
||||||
|
|
||||||
thread.HandlePostSyscall();
|
thread.HandlePostSyscall();
|
||||||
|
|
||||||
@@ -129,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
replyTargetHandle = 0;
|
replyTargetHandle = 0;
|
||||||
|
|
||||||
if (rc == Result.Success && signaledIndex >= portHandles.Length)
|
if (rc == Result.Success && signaledIndex >= portHandleCount)
|
||||||
{
|
{
|
||||||
// We got a IPC request, process it, pass to the appropriate service if needed.
|
// We got a IPC request, process it, pass to the appropriate service if needed.
|
||||||
int signaledHandle = handles[signaledIndex];
|
int signaledHandle = handles[signaledIndex];
|
||||||
@@ -156,6 +188,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<int>.Shared.Return(handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
@@ -166,13 +200,9 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
KThread thread = KernelStatic.GetCurrentThread();
|
||||||
ulong messagePtr = thread.TlsAddress;
|
ulong messagePtr = thread.TlsAddress;
|
||||||
ulong messageSize = 0x100;
|
|
||||||
|
|
||||||
byte[] reqData = new byte[messageSize];
|
IpcMessage request = ReadRequest(process, messagePtr);
|
||||||
|
|
||||||
process.CpuMemory.Read(messagePtr, reqData);
|
|
||||||
|
|
||||||
IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
|
|
||||||
IpcMessage response = new IpcMessage();
|
IpcMessage response = new IpcMessage();
|
||||||
|
|
||||||
ulong tempAddr = recvListAddr;
|
ulong tempAddr = recvListAddr;
|
||||||
@@ -202,158 +232,160 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
bool shouldReply = true;
|
bool shouldReply = true;
|
||||||
bool isTipcCommunication = false;
|
bool isTipcCommunication = false;
|
||||||
|
|
||||||
using (MemoryStream raw = new MemoryStream(request.RawData))
|
_requestDataStream.SetLength(0);
|
||||||
|
_requestDataStream.Write(request.RawData);
|
||||||
|
_requestDataStream.Position = 0;
|
||||||
|
|
||||||
|
if (request.Type == IpcMessageType.HipcRequest ||
|
||||||
|
request.Type == IpcMessageType.HipcRequestWithContext)
|
||||||
{
|
{
|
||||||
BinaryReader reqReader = new BinaryReader(raw);
|
response.Type = IpcMessageType.HipcResponse;
|
||||||
|
|
||||||
if (request.Type == IpcMessageType.HipcRequest ||
|
_responseDataStream.SetLength(0);
|
||||||
request.Type == IpcMessageType.HipcRequestWithContext)
|
|
||||||
|
ServiceCtx context = new ServiceCtx(
|
||||||
|
_context.Device,
|
||||||
|
process,
|
||||||
|
process.CpuMemory,
|
||||||
|
thread,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
_requestDataReader,
|
||||||
|
_responseDataWriter);
|
||||||
|
|
||||||
|
_sessions[serverSessionHandle].CallHipcMethod(context);
|
||||||
|
|
||||||
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
}
|
||||||
|
else if (request.Type == IpcMessageType.HipcControl ||
|
||||||
|
request.Type == IpcMessageType.HipcControlWithContext)
|
||||||
|
{
|
||||||
|
uint magic = (uint)_requestDataReader.ReadUInt64();
|
||||||
|
uint cmdId = (uint)_requestDataReader.ReadUInt64();
|
||||||
|
|
||||||
|
switch (cmdId)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
case 0:
|
||||||
|
FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
||||||
|
break;
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
case 3:
|
||||||
{
|
FillHipcResponse(response, 0, PointerBufferSize);
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
break;
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||||
_context.Device,
|
case 2:
|
||||||
process,
|
case 4:
|
||||||
process.CpuMemory,
|
int unknown = _requestDataReader.ReadInt32();
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallHipcMethod(context);
|
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
||||||
}
|
|
||||||
|
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
||||||
|
|
||||||
|
FillHipcResponse(response, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: throw new NotImplementedException(cmdId.ToString());
|
||||||
}
|
}
|
||||||
else if (request.Type == IpcMessageType.HipcControl ||
|
}
|
||||||
request.Type == IpcMessageType.HipcControlWithContext)
|
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
||||||
|
{
|
||||||
|
_context.Syscall.CloseHandle(serverSessionHandle);
|
||||||
|
lock (_handleLock)
|
||||||
{
|
{
|
||||||
uint magic = (uint)reqReader.ReadUInt64();
|
|
||||||
uint cmdId = (uint)reqReader.ReadUInt64();
|
|
||||||
|
|
||||||
switch (cmdId)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
request = FillResponse(response, 0, PointerBufferSize);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
int unknown = reqReader.ReadInt32();
|
|
||||||
|
|
||||||
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
|
||||||
|
|
||||||
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
|
||||||
|
|
||||||
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
|
||||||
|
|
||||||
request = FillResponse(response, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: throw new NotImplementedException(cmdId.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
|
||||||
{
|
|
||||||
_context.Syscall.CloseHandle(serverSessionHandle);
|
|
||||||
_sessionHandles.Remove(serverSessionHandle);
|
_sessionHandles.Remove(serverSessionHandle);
|
||||||
IpcService service = _sessions[serverSessionHandle];
|
|
||||||
if (service is IDisposable disposableObj)
|
|
||||||
{
|
|
||||||
disposableObj.Dispose();
|
|
||||||
}
|
|
||||||
_sessions.Remove(serverSessionHandle);
|
|
||||||
shouldReply = false;
|
|
||||||
}
|
}
|
||||||
// If the type is past 0xF, we are using TIPC
|
IpcService service = _sessions[serverSessionHandle];
|
||||||
else if (request.Type > IpcMessageType.TipcCloseSession)
|
(service as IDisposable)?.Dispose();
|
||||||
{
|
_sessions.Remove(serverSessionHandle);
|
||||||
isTipcCommunication = true;
|
shouldReply = false;
|
||||||
|
|
||||||
// Response type is always the same as request on TIPC.
|
|
||||||
response.Type = request.Type;
|
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
|
||||||
_context.Device,
|
|
||||||
process,
|
|
||||||
process.CpuMemory,
|
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallTipcMethod(context);
|
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotImplementedException(request.Type.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTipcCommunication)
|
|
||||||
{
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldReply;
|
|
||||||
}
|
}
|
||||||
}
|
// If the type is past 0xF, we are using TIPC
|
||||||
|
else if (request.Type > IpcMessageType.TipcCloseSession)
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
isTipcCommunication = true;
|
||||||
|
|
||||||
foreach (int value in values)
|
// Response type is always the same as request on TIPC.
|
||||||
{
|
response.Type = request.Type;
|
||||||
writer.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FillResponse(response, result, ms.ToArray());
|
_responseDataStream.SetLength(0);
|
||||||
|
|
||||||
|
ServiceCtx context = new ServiceCtx(
|
||||||
|
_context.Device,
|
||||||
|
process,
|
||||||
|
process.CpuMemory,
|
||||||
|
thread,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
_requestDataReader,
|
||||||
|
_responseDataWriter);
|
||||||
|
|
||||||
|
_sessions[serverSessionHandle].CallTipcMethod(context);
|
||||||
|
|
||||||
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
|
||||||
|
using var responseStream = response.GetStreamTipc();
|
||||||
|
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException(request.Type.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTipcCommunication)
|
||||||
|
{
|
||||||
|
using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
|
||||||
|
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
|
private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
|
||||||
|
{
|
||||||
|
const int messageSize = 0x100;
|
||||||
|
|
||||||
|
byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
|
||||||
|
|
||||||
|
Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
|
||||||
|
reqDataSpan.Clear();
|
||||||
|
|
||||||
|
process.CpuMemory.Read(messagePtr, reqDataSpan);
|
||||||
|
|
||||||
|
IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
|
||||||
|
|
||||||
|
ArrayPool<byte>.Shared.Return(reqData);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result)
|
||||||
|
{
|
||||||
|
FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result, int value)
|
||||||
|
{
|
||||||
|
Span<byte> span = stackalloc byte[sizeof(int)];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, value);
|
||||||
|
FillHipcResponse(response, result, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
response.Type = IpcMessageType.HipcResponse;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
_responseDataStream.SetLength(0);
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
writer.Write(IpcMagic.Sfco);
|
_responseDataStream.Write(IpcMagic.Sfco);
|
||||||
writer.Write(result);
|
_responseDataStream.Write(result);
|
||||||
|
|
||||||
if (data != null)
|
_responseDataStream.Write(data);
|
||||||
{
|
|
||||||
writer.Write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.RawData = ms.ToArray();
|
response.RawData = _responseDataStream.ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
@@ -372,6 +404,11 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
_sessions.Clear();
|
_sessions.Clear();
|
||||||
|
|
||||||
|
_requestDataReader.Dispose();
|
||||||
|
_requestDataStream.Dispose();
|
||||||
|
_responseDataWriter.Dispose();
|
||||||
|
_responseDataStream.Dispose();
|
||||||
|
|
||||||
InitDone.Dispose();
|
InitDone.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
public uint DataSize { get; }
|
public uint DataSize { get; }
|
||||||
public uint BssSize { get; }
|
public uint BssSize { get; }
|
||||||
|
|
||||||
public int[] Capabilities { get; }
|
public uint[] Capabilities { get; }
|
||||||
public bool UsesSecureMemory { get; }
|
public bool UsesSecureMemory { get; }
|
||||||
public bool Is64BitAddressSpace { get; }
|
public bool Is64BitAddressSpace { get; }
|
||||||
public bool Is64Bit { get; }
|
public bool Is64Bit { get; }
|
||||||
@@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
Version = reader.Version;
|
Version = reader.Version;
|
||||||
Name = reader.Name.ToString();
|
Name = reader.Name.ToString();
|
||||||
|
|
||||||
Capabilities = new int[32];
|
Capabilities = new uint[32];
|
||||||
|
|
||||||
for (int index = 0; index < Capabilities.Length; index++)
|
for (int index = 0; index < Capabilities.Length; index++)
|
||||||
{
|
{
|
||||||
Capabilities[index] = (int)reader.Capabilities[index];
|
Capabilities[index] = reader.Capabilities[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();
|
reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -77,7 +79,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ulong position = context.Request.PtrBuff[index].Position;
|
ulong position = context.Request.PtrBuff[index].Position;
|
||||||
ulong size = context.Request.PtrBuff[index].Size;
|
ulong size = context.Request.PtrBuff[index].Size;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
while (size-- > 0)
|
while (size-- > 0)
|
||||||
{
|
{
|
||||||
@@ -91,7 +93,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(ms.ToArray());
|
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ulong position = context.Request.SendBuff[index].Position;
|
ulong position = context.Request.SendBuff[index].Position;
|
||||||
ulong size = context.Request.SendBuff[index].Size;
|
ulong size = context.Request.SendBuff[index].Size;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
while (size-- > 0)
|
while (size-- > 0)
|
||||||
{
|
{
|
||||||
@@ -124,7 +126,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(ms.ToArray());
|
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.Input.Motion.CemuHook.Protocol;
|
using Ryujinx.Input.Motion.CemuHook.Protocol;
|
||||||
using System;
|
using System;
|
||||||
@@ -381,7 +382,7 @@ namespace Ryujinx.Input.Motion.CemuHook
|
|||||||
|
|
||||||
Header header = GenerateHeader(clientId);
|
Header header = GenerateHeader(clientId);
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(header);
|
writer.WriteStruct(header);
|
||||||
@@ -421,7 +422,7 @@ namespace Ryujinx.Input.Motion.CemuHook
|
|||||||
|
|
||||||
Header header = GenerateHeader(clientId);
|
Header header = GenerateHeader(clientId);
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(header);
|
writer.WriteStruct(header);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Memory
|
namespace Ryujinx.Memory
|
||||||
@@ -77,6 +78,21 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
||||||
void Write(ulong va, ReadOnlySpan<byte> data);
|
void Write(ulong va, ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data to CPU mapped memory, with write tracking.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">Virtual address to write the data into</param>
|
||||||
|
/// <param name="data">Data to be written</param>
|
||||||
|
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
||||||
|
public void Write(ulong va, ReadOnlySequence<byte> data)
|
||||||
|
{
|
||||||
|
foreach (ReadOnlyMemory<byte> segment in data)
|
||||||
|
{
|
||||||
|
Write(va, segment.Span);
|
||||||
|
va += (ulong)segment.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes data to the application process, returning false if the data was not changed.
|
/// Writes data to the application process, returning false if the data was not changed.
|
||||||
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
|
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
|
||||||
|
@@ -20,16 +20,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Count => HasSingleRange ? 1 : _ranges.Length;
|
public int Count => HasSingleRange ? 1 : _ranges.Length;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Minimum start address of all sub-ranges.
|
|
||||||
/// </summary>
|
|
||||||
public ulong MinAddress { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum end address of all sub-ranges.
|
|
||||||
/// </summary>
|
|
||||||
public ulong MaxAddress { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new multi-range with a single physical region.
|
/// Creates a new multi-range with a single physical region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,8 +29,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
_singleRange = new MemoryRange(address, size);
|
_singleRange = new MemoryRange(address, size);
|
||||||
_ranges = null;
|
_ranges = null;
|
||||||
MinAddress = address;
|
|
||||||
MaxAddress = address + size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -52,30 +40,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
_singleRange = MemoryRange.Empty;
|
_singleRange = MemoryRange.Empty;
|
||||||
_ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));
|
_ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));
|
||||||
|
|
||||||
if (ranges.Length != 0)
|
|
||||||
{
|
|
||||||
MinAddress = ulong.MaxValue;
|
|
||||||
MaxAddress = 0UL;
|
|
||||||
|
|
||||||
foreach (MemoryRange range in ranges)
|
|
||||||
{
|
|
||||||
if (MinAddress > range.Address)
|
|
||||||
{
|
|
||||||
MinAddress = range.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MaxAddress < range.EndAddress)
|
|
||||||
{
|
|
||||||
MaxAddress = range.EndAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MinAddress = 0UL;
|
|
||||||
MaxAddress = 0UL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -84,7 +48,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="offset">Offset of the slice into the multi-range in bytes</param>
|
/// <param name="offset">Offset of the slice into the multi-range in bytes</param>
|
||||||
/// <param name="size">Size of the slice in bytes</param>
|
/// <param name="size">Size of the slice in bytes</param>
|
||||||
/// <returns>A new multi-range representing the given slice of this one</returns>
|
/// <returns>A new multi-range representing the given slice of this one</returns>
|
||||||
public MultiRange GetSlice(ulong offset, ulong size)
|
public MultiRange Slice(ulong offset, ulong size)
|
||||||
{
|
{
|
||||||
if (HasSingleRange)
|
if (HasSingleRange)
|
||||||
{
|
{
|
||||||
|
@@ -6,6 +6,7 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@@ -136,8 +137,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (MemoryStream streamPng = new MemoryStream())
|
using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
file.Get.AsStream().CopyTo(stream);
|
file.Get.AsStream().CopyTo(stream);
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
private byte[] ProcessImage(byte[] data)
|
private byte[] ProcessImage(byte[] data)
|
||||||
{
|
{
|
||||||
using (MemoryStream streamJpg = new MemoryStream())
|
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Image avatarImage = Image.Load(data, new PngDecoder());
|
Image avatarImage = Image.Load(data, new PngDecoder());
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Gtk;
|
using Gtk;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
@@ -181,7 +182,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
{
|
{
|
||||||
image.Mutate(x => x.Resize(256, 256));
|
image.Mutate(x => x.Resize(256, 256));
|
||||||
|
|
||||||
using (MemoryStream streamJpg = new MemoryStream())
|
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
image.SaveAsJpeg(streamJpg);
|
image.SaveAsJpeg(streamJpg);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user