Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
43b4b34376 | ||
|
92ca1cb0cb | ||
|
50d7ecf76d | ||
|
42a2a80b87 | ||
|
54deded929 | ||
|
39bdf6d41e | ||
|
074190e03c | ||
|
256514c7c9 | ||
|
556be08c4e | ||
|
1cbca5eecb | ||
|
95017b8c66 | ||
|
4a892fbdc9 | ||
|
9eb5b7a10d | ||
|
d64594ec74 | ||
|
6a1a03566a | ||
|
13f5294aa3 | ||
|
9444b4a647 |
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -46,6 +46,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.1.0"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-dotnet@v1
|
- uses: actions/setup-dotnet@v1
|
||||||
@@ -59,24 +60,24 @@ jobs:
|
|||||||
- name: Clear
|
- name: Clear
|
||||||
run: dotnet clean && dotnet nuget locals all --clear
|
run: dotnet clean && dotnet nuget locals all --clear
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.1.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test -c "${{ matrix.configuration }}"
|
run: dotnet test -c "${{ matrix.configuration }}"
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish
|
path: publish
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: ryujinx-headless-sdl2-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: ryujinx-headless-sdl2-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_sdl2_headless
|
path: publish_sdl2_headless
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
25
ARMeilleure/Decoders/OpCodeT32MemImm12.cs
Normal file
25
ARMeilleure/Decoders/OpCodeT32MemImm12.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32MemImm12 : OpCodeT32, IOpCode32Mem
|
||||||
|
{
|
||||||
|
public int Rt { get; }
|
||||||
|
public int Rn { get; }
|
||||||
|
public bool WBack => false;
|
||||||
|
public bool IsLoad { get; }
|
||||||
|
public bool Index => true;
|
||||||
|
public bool Add => true;
|
||||||
|
public int Immediate { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MemImm12(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32MemImm12(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rt = (opCode >> 12) & 0xf;
|
||||||
|
Rn = (opCode >> 16) & 0xf;
|
||||||
|
|
||||||
|
Immediate = opCode & 0xfff;
|
||||||
|
|
||||||
|
IsLoad = ((opCode >> 20) & 1) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
ARMeilleure/Decoders/OpCodeT32MemImm8.cs
Normal file
29
ARMeilleure/Decoders/OpCodeT32MemImm8.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32MemImm8 : OpCodeT32, IOpCode32Mem
|
||||||
|
{
|
||||||
|
public int Rt { get; }
|
||||||
|
public int Rn { get; }
|
||||||
|
public bool WBack { get; }
|
||||||
|
public bool IsLoad { get; }
|
||||||
|
public bool Index { get; }
|
||||||
|
public bool Add { get; }
|
||||||
|
public int Immediate { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MemImm8(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32MemImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rt = (opCode >> 12) & 0xf;
|
||||||
|
Rn = (opCode >> 16) & 0xf;
|
||||||
|
|
||||||
|
Index = ((opCode >> 10) & 1) != 0;
|
||||||
|
Add = ((opCode >> 9) & 1) != 0;
|
||||||
|
WBack = ((opCode >> 8) & 1) != 0;
|
||||||
|
|
||||||
|
Immediate = opCode & 0xff;
|
||||||
|
|
||||||
|
IsLoad = ((opCode >> 20) & 1) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1065,6 +1065,26 @@ namespace ARMeilleure.Decoders
|
|||||||
SetT32("11110x011011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluImm.Create);
|
SetT32("11110x011011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluImm.Create);
|
||||||
SetT32("11101010100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluRsImm.Create);
|
SetT32("11101010100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluRsImm.Create);
|
||||||
SetT32("11110x00100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluImm.Create);
|
SetT32("11110x00100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("111110000101xxxx<<<<10x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000101xxxx<<<<1100xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000101xxxx<<<<11x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001101xxxxxxxxxxxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110000001xxxx<<<<10x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000001xxxx<<<<1100xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000001xxxx<<<<11x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001001xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110000011xxxx<<<<10x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000011xxxx<<<<1100xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110000011xxxx<<<<11x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001011xxxxxxxxxxxxxxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110010001xxxx<<<<10x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110010001xxxx<<<<1100xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110010001xxxx<<<<11x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110011001xxxxxxxxxxxxxxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110010011xxxx<<<<10x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110010011xxxx<<<<1100xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110010011xxxx<<<<11x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110011011xxxxxxxxxxxxxxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm12.Create);
|
||||||
SetT32("11101010010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluRsImm.Create);
|
SetT32("11101010010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluRsImm.Create);
|
||||||
SetT32("11110x00010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluImm.Create);
|
SetT32("11110x00010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluImm.Create);
|
||||||
SetT32("11101010011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluRsImm.Create);
|
SetT32("11101010011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluRsImm.Create);
|
||||||
@@ -1077,6 +1097,12 @@ namespace ARMeilleure.Decoders
|
|||||||
SetT32("11110x01110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluImm.Create);
|
SetT32("11110x01110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluImm.Create);
|
||||||
SetT32("11101011011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluRsImm.Create);
|
SetT32("11101011011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluRsImm.Create);
|
||||||
SetT32("11110x01011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluImm.Create);
|
SetT32("11110x01011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("111110000100xxxxxxxx1<<>xxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001100xxxxxxxxxxxxxxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110000000xxxxxxxx1<<>xxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001000xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT32MemImm12.Create);
|
||||||
|
SetT32("111110000010xxxxxxxx1<<>xxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT32MemImm8.Create);
|
||||||
|
SetT32("111110001010xxxxxxxxxxxxxxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT32MemImm12.Create);
|
||||||
SetT32("11101011101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluRsImm.Create);
|
SetT32("11101011101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluRsImm.Create);
|
||||||
SetT32("11110x01101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluImm.Create);
|
SetT32("11110x01101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluImm.Create);
|
||||||
SetT32("111010101001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluRsImm.Create);
|
SetT32("111010101001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluRsImm.Create);
|
||||||
|
@@ -103,7 +103,7 @@ namespace ARMeilleure.Signal
|
|||||||
// Unix siginfo struct locations.
|
// Unix siginfo struct locations.
|
||||||
// NOTE: These are incredibly likely to be different between kernel version and architectures.
|
// NOTE: These are incredibly likely to be different between kernel version and architectures.
|
||||||
|
|
||||||
config.StructAddressOffset = 16; // si_addr
|
config.StructAddressOffset = OperatingSystem.IsMacOS() ? 24 : 16; // si_addr
|
||||||
config.StructWriteOffset = 8; // si_code
|
config.StructWriteOffset = 8; // si_code
|
||||||
|
|
||||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||||
|
@@ -21,6 +21,7 @@ namespace ARMeilleure.Signal
|
|||||||
static class UnixSignalHandlerRegistration
|
static class UnixSignalHandlerRegistration
|
||||||
{
|
{
|
||||||
private const int SIGSEGV = 11;
|
private const int SIGSEGV = 11;
|
||||||
|
private const int SIGBUS = 10;
|
||||||
private const int SA_SIGINFO = 0x00000004;
|
private const int SA_SIGINFO = 0x00000004;
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
[DllImport("libc", SetLastError = true)]
|
||||||
@@ -43,7 +44,17 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Could not register sigaction. Error: {result}");
|
throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
result = sigaction(SIGBUS, ref sig, out SigAction oldb);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
@@ -51,7 +62,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
public static bool RestoreExceptionHandler(SigAction oldAction)
|
public static bool RestoreExceptionHandler(SigAction oldAction)
|
||||||
{
|
{
|
||||||
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0;
|
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="K">Key</typeparam>
|
/// <typeparam name="K">Key</typeparam>
|
||||||
/// <typeparam name="V">Value</typeparam>
|
/// <typeparam name="V">Value</typeparam>
|
||||||
public class IntervalTree<K, V> where K : IComparable<K>
|
class IntervalTree<K, V> where K : IComparable<K>
|
||||||
{
|
{
|
||||||
private const int ArrayGrowthSize = 32;
|
private const int ArrayGrowthSize = 32;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Number of intervals found</returns>
|
/// <returns>Number of intervals found</returns>
|
||||||
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
|
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
|
||||||
{
|
{
|
||||||
GetValues(_root, start, end, ref overlaps, ref overlapCount);
|
GetKeys(_root, start, end, ref overlaps, ref overlapCount);
|
||||||
|
|
||||||
return overlapCount;
|
return overlapCount;
|
||||||
}
|
}
|
||||||
@@ -180,20 +180,20 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve all values that overlap the given start and end keys.
|
/// Retrieve all keys that overlap the given start and end keys.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="start">Start of the range</param>
|
/// <param name="start">Start of the range</param>
|
||||||
/// <param name="end">End of the range</param>
|
/// <param name="end">End of the range</param>
|
||||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
/// <param name="overlaps">Overlaps array to place results in</param>
|
||||||
/// <param name="overlapCount">Overlaps count to update</param>
|
/// <param name="overlapCount">Overlaps count to update</param>
|
||||||
private void GetValues(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
|
private void GetKeys(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
|
||||||
{
|
{
|
||||||
if (node == null || start.CompareTo(node.Max) >= 0)
|
if (node == null || start.CompareTo(node.Max) >= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetValues(node.Left, start, end, ref overlaps, ref overlapCount);
|
GetKeys(node.Left, start, end, ref overlaps, ref overlapCount);
|
||||||
|
|
||||||
bool endsOnRight = end.CompareTo(node.Start) > 0;
|
bool endsOnRight = end.CompareTo(node.Start) > 0;
|
||||||
if (endsOnRight)
|
if (endsOnRight)
|
||||||
@@ -208,7 +208,7 @@ namespace ARMeilleure.Translation
|
|||||||
overlaps[overlapCount++] = node.Start;
|
overlaps[overlapCount++] = node.Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetValues(node.Right, start, end, ref overlaps, ref overlapCount);
|
GetKeys(node.Right, start, end, ref overlaps, ref overlapCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,40 +717,40 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="K">Key type of the node</typeparam>
|
/// <typeparam name="K">Key type of the node</typeparam>
|
||||||
/// <typeparam name="V">Value type of the node</typeparam>
|
/// <typeparam name="V">Value type of the node</typeparam>
|
||||||
internal class IntervalTreeNode<K, V>
|
class IntervalTreeNode<K, V>
|
||||||
{
|
{
|
||||||
internal bool Color = true;
|
public bool Color = true;
|
||||||
internal IntervalTreeNode<K, V> Left = null;
|
public IntervalTreeNode<K, V> Left = null;
|
||||||
internal IntervalTreeNode<K, V> Right = null;
|
public IntervalTreeNode<K, V> Right = null;
|
||||||
internal IntervalTreeNode<K, V> Parent = null;
|
public IntervalTreeNode<K, V> Parent = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The start of the range.
|
/// The start of the range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K Start;
|
public K Start;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The end of the range.
|
/// The end of the range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K End;
|
public K End;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum end value of this node and all its children.
|
/// The maximum end value of this node and all its children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K Max;
|
public K Max;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Value stored on this node.
|
/// Value stored on this node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal V Value;
|
public V Value;
|
||||||
|
|
||||||
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
||||||
{
|
{
|
||||||
this.Start = start;
|
Start = start;
|
||||||
this.End = end;
|
End = end;
|
||||||
this.Max = end;
|
Max = end;
|
||||||
this.Value = value;
|
Value = value;
|
||||||
this.Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Collections
|
namespace Ryujinx.Common.Collections
|
||||||
@@ -779,37 +777,37 @@ namespace Ryujinx.Common.Collections
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="K">Key type of the node</typeparam>
|
/// <typeparam name="K">Key type of the node</typeparam>
|
||||||
/// <typeparam name="V">Value type of the node</typeparam>
|
/// <typeparam name="V">Value type of the node</typeparam>
|
||||||
internal class IntervalTreeNode<K, V>
|
class IntervalTreeNode<K, V>
|
||||||
{
|
{
|
||||||
internal bool Color = true;
|
public bool Color = true;
|
||||||
internal IntervalTreeNode<K, V> Left = null;
|
public IntervalTreeNode<K, V> Left = null;
|
||||||
internal IntervalTreeNode<K, V> Right = null;
|
public IntervalTreeNode<K, V> Right = null;
|
||||||
internal IntervalTreeNode<K, V> Parent = null;
|
public IntervalTreeNode<K, V> Parent = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The start of the range.
|
/// The start of the range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K Start;
|
public K Start;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The end of the range - maximum of all in the Values list.
|
/// The end of the range - maximum of all in the Values list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K End;
|
public K End;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum end value of this node and all its children.
|
/// The maximum end value of this node and all its children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal K Max;
|
public K Max;
|
||||||
|
|
||||||
internal List<RangeNode<K, V>> Values;
|
public List<RangeNode<K, V>> Values;
|
||||||
|
|
||||||
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
||||||
{
|
{
|
||||||
this.Start = start;
|
Start = start;
|
||||||
this.End = end;
|
End = end;
|
||||||
this.Max = end;
|
Max = end;
|
||||||
this.Values = new List<RangeNode<K, V>> { new RangeNode<K, V>(start, end, value) };
|
Values = new List<RangeNode<K, V>> { new RangeNode<K, V>(start, end, value) };
|
||||||
this.Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -967,20 +967,20 @@ namespace Ryujinx.Common.Collections
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="K">Key of the node</typeparam>
|
/// <typeparam name="K">Key of the node</typeparam>
|
||||||
/// <typeparam name="V">Value of the node</typeparam>
|
/// <typeparam name="V">Value of the node</typeparam>
|
||||||
internal class Node<K, V>
|
class Node<K, V>
|
||||||
{
|
{
|
||||||
internal bool Color = true;
|
public bool Color = true;
|
||||||
internal Node<K, V> Left = null;
|
public Node<K, V> Left = null;
|
||||||
internal Node<K, V> Right = null;
|
public Node<K, V> Right = null;
|
||||||
internal Node<K, V> Parent = null;
|
public Node<K, V> Parent = null;
|
||||||
internal K Key;
|
public K Key;
|
||||||
internal V Value;
|
public V Value;
|
||||||
|
|
||||||
public Node(K key, V value, Node<K, V> parent)
|
public Node(K key, V value, Node<K, V> parent)
|
||||||
{
|
{
|
||||||
this.Key = key;
|
Key = key;
|
||||||
this.Value = value;
|
Value = value;
|
||||||
this.Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ namespace Ryujinx.Cpu
|
|||||||
_baseAddress = (ulong)_addressSpace.Pointer;
|
_baseAddress = (ulong)_addressSpace.Pointer;
|
||||||
ulong endAddress = _baseAddress + addressSpace.Size;
|
ulong endAddress = _baseAddress + addressSpace.Size;
|
||||||
|
|
||||||
_trackingEvent = new TrackingEventDelegate(tracking.VirtualMemoryEvent);
|
_trackingEvent = new TrackingEventDelegate(tracking.VirtualMemoryEventEh);
|
||||||
bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||||
|
|
||||||
if (!added)
|
if (!added)
|
||||||
|
@@ -25,6 +25,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
private const int PointerTagBit = 62;
|
private const int PointerTagBit = 62;
|
||||||
|
|
||||||
|
private readonly MemoryBlock _backingMemory;
|
||||||
private readonly InvalidAccessHandler _invalidAccessHandler;
|
private readonly InvalidAccessHandler _invalidAccessHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -50,10 +51,12 @@ namespace Ryujinx.Cpu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the memory manager.
|
/// Creates a new instance of the memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
||||||
/// <param name="addressSpaceSize">Size of the address space</param>
|
/// <param name="addressSpaceSize">Size of the address space</param>
|
||||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||||
public MemoryManager(ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null)
|
public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null)
|
||||||
{
|
{
|
||||||
|
_backingMemory = backingMemory;
|
||||||
_invalidAccessHandler = invalidAccessHandler;
|
_invalidAccessHandler = invalidAccessHandler;
|
||||||
|
|
||||||
ulong asSize = PageSize;
|
ulong asSize = PageSize;
|
||||||
@@ -73,18 +76,19 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Map(ulong va, nuint hostAddress, ulong size)
|
public void Map(ulong va, ulong pa, ulong size)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
ulong remainingSize = size;
|
ulong remainingSize = size;
|
||||||
ulong oVa = va;
|
ulong oVa = va;
|
||||||
|
ulong oPa = pa;
|
||||||
while (remainingSize != 0)
|
while (remainingSize != 0)
|
||||||
{
|
{
|
||||||
_pageTable.Write((va / PageSize) * PteSize, hostAddress);
|
_pageTable.Write((va / PageSize) * PteSize, PaToPte(pa));
|
||||||
|
|
||||||
va += PageSize;
|
va += PageSize;
|
||||||
hostAddress += PageSize;
|
pa += PageSize;
|
||||||
remainingSize -= PageSize;
|
remainingSize -= PageSize;
|
||||||
}
|
}
|
||||||
Tracking.Map(oVa, size);
|
Tracking.Map(oVa, size);
|
||||||
@@ -107,7 +111,7 @@ namespace Ryujinx.Cpu
|
|||||||
ulong remainingSize = size;
|
ulong remainingSize = size;
|
||||||
while (remainingSize != 0)
|
while (remainingSize != 0)
|
||||||
{
|
{
|
||||||
_pageTable.Write((va / PageSize) * PteSize, (nuint)0);
|
_pageTable.Write((va / PageSize) * PteSize, 0UL);
|
||||||
|
|
||||||
va += PageSize;
|
va += PageSize;
|
||||||
remainingSize -= PageSize;
|
remainingSize -= PageSize;
|
||||||
@@ -122,9 +126,22 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public T ReadTracked<T>(ulong va) where T : unmanaged
|
public T ReadTracked<T>(ulong va) where T : unmanaged
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
|
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
|
||||||
return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
|
|
||||||
|
return Read<T>(va);
|
||||||
|
}
|
||||||
|
catch (InvalidMemoryRegionException)
|
||||||
|
{
|
||||||
|
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -177,7 +194,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
if (IsContiguousAndMapped(va, data.Length))
|
if (IsContiguousAndMapped(va, data.Length))
|
||||||
{
|
{
|
||||||
data.CopyTo(GetHostSpanContiguous(va, data.Length));
|
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -185,18 +202,22 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size));
|
data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, PageSize);
|
size = Math.Min(data.Length - offset, PageSize);
|
||||||
|
|
||||||
data.Slice(offset, size).CopyTo(GetHostSpanContiguous(va + (ulong)offset, size));
|
data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +245,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
if (IsContiguousAndMapped(va, size))
|
if (IsContiguousAndMapped(va, size))
|
||||||
{
|
{
|
||||||
return GetHostSpanContiguous(va, size);
|
return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -251,7 +272,7 @@ namespace Ryujinx.Cpu
|
|||||||
SignalMemoryTracking(va, (ulong)size, true);
|
SignalMemoryTracking(va, (ulong)size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WritableRegion(null, va, new NativeMemoryManager<byte>((byte*)GetHostAddress(va), size).Memory);
|
return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -264,7 +285,7 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public unsafe ref T GetRef<T>(ulong va) where T : unmanaged
|
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||||
{
|
{
|
||||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||||
{
|
{
|
||||||
@@ -273,7 +294,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||||
|
|
||||||
return ref *(T*)GetHostAddress(va);
|
return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -293,7 +314,7 @@ namespace Ryujinx.Cpu
|
|||||||
return (int)(vaSpan / PageSize);
|
return (int)(vaSpan / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
|
private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
|
private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
|
||||||
@@ -315,7 +336,7 @@ namespace Ryujinx.Cpu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize))
|
if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -327,11 +348,11 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<HostMemoryRange>();
|
return Enumerable.Empty<MemoryRange>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||||
@@ -341,9 +362,9 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
var regions = new List<HostMemoryRange>();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
nuint regionStart = GetHostAddress(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
@@ -353,12 +374,12 @@ namespace Ryujinx.Cpu
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nuint newHostAddress = GetHostAddress(va + PageSize);
|
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||||
|
|
||||||
if (GetHostAddress(va) + PageSize != newHostAddress)
|
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new HostMemoryRange(regionStart, regionSize));
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
regionStart = newHostAddress;
|
regionStart = newPa;
|
||||||
regionSize = 0;
|
regionSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,7 +387,7 @@ namespace Ryujinx.Cpu
|
|||||||
regionSize += PageSize;
|
regionSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.Add(new HostMemoryRange(regionStart, regionSize));
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
|
|
||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
@@ -386,18 +407,22 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size));
|
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size));
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, PageSize);
|
size = Math.Min(data.Length - offset, PageSize);
|
||||||
|
|
||||||
GetHostSpanContiguous(va + (ulong)offset, size).CopyTo(data.Slice(offset, size));
|
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidMemoryRegionException)
|
catch (InvalidMemoryRegionException)
|
||||||
@@ -446,7 +471,7 @@ namespace Ryujinx.Cpu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _pageTable.Read<nuint>((va / PageSize) * PteSize) != 0;
|
return _pageTable.Read<ulong>((va / PageSize) * PteSize) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateAddress(ulong va)
|
private bool ValidateAddress(ulong va)
|
||||||
@@ -480,37 +505,20 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private ulong GetPhysicalAddress(ulong va)
|
||||||
/// Get a span representing the given virtual address and size range in host memory.
|
|
||||||
/// This function assumes that the requested virtual memory region is contiguous.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the range</param>
|
|
||||||
/// <param name="size">Size of the range in bytes</param>
|
|
||||||
/// <returns>A span representing the given virtual range in host memory</returns>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw when the base virtual address is not mapped</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private unsafe Span<byte> GetHostSpanContiguous(ulong va, int size)
|
|
||||||
{
|
{
|
||||||
return new Span<byte>((void*)GetHostAddress(va), size);
|
// We return -1L if the virtual address is invalid or unmapped.
|
||||||
|
if (!ValidateAddress(va) || !IsMapped(va))
|
||||||
|
{
|
||||||
|
return ulong.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
return GetPhysicalAddressInternal(va);
|
||||||
/// Get the host address for a given virtual address, using the page table.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address</param>
|
|
||||||
/// <returns>The corresponding host address for the given virtual address</returns>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw when the virtual address is not mapped</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private nuint GetHostAddress(ulong va)
|
|
||||||
{
|
|
||||||
nuint pageBase = _pageTable.Read<nuint>((va / PageSize) * PteSize) & unchecked((nuint)0xffff_ffff_ffffUL);
|
|
||||||
|
|
||||||
if (pageBase == 0)
|
|
||||||
{
|
|
||||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pageBase + (nuint)(va & PageMask);
|
private ulong GetPhysicalAddressInternal(ulong va)
|
||||||
|
{
|
||||||
|
return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -604,6 +612,16 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ulong PaToPte(ulong pa)
|
||||||
|
{
|
||||||
|
return (ulong)_backingMemory.GetPointer(pa, PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong PteToPa(ulong pte)
|
||||||
|
{
|
||||||
|
return (ulong)((long)pte - _backingMemory.Pointer.ToInt64());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes of resources used by the memory manager.
|
/// Disposes of resources used by the memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -5,7 +5,6 @@ using Ryujinx.Memory.Range;
|
|||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ namespace Ryujinx.Cpu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked
|
public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = 12;
|
public const int PageBits = 12;
|
||||||
public const int PageSize = 1 << PageBits;
|
public const int PageSize = 1 << PageBits;
|
||||||
@@ -42,9 +41,12 @@ namespace Ryujinx.Cpu
|
|||||||
private readonly MemoryBlock _addressSpaceMirror;
|
private readonly MemoryBlock _addressSpaceMirror;
|
||||||
private readonly ulong _addressSpaceSize;
|
private readonly ulong _addressSpaceSize;
|
||||||
|
|
||||||
|
private readonly MemoryBlock _backingMemory;
|
||||||
|
private readonly PageTable<ulong> _pageTable;
|
||||||
|
|
||||||
private readonly MemoryEhMeilleure _memoryEh;
|
private readonly MemoryEhMeilleure _memoryEh;
|
||||||
|
|
||||||
private ulong[] _pageTable;
|
private readonly ulong[] _pageBitmap;
|
||||||
|
|
||||||
public int AddressSpaceBits { get; }
|
public int AddressSpaceBits { get; }
|
||||||
|
|
||||||
@@ -59,11 +61,14 @@ namespace Ryujinx.Cpu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the host mapped memory manager.
|
/// Creates a new instance of the host mapped memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
||||||
/// <param name="addressSpaceSize">Size of the address space</param>
|
/// <param name="addressSpaceSize">Size of the address space</param>
|
||||||
/// <param name="unsafeMode">True if unmanaged access should not be masked (unsafe), false otherwise.</param>
|
/// <param name="unsafeMode">True if unmanaged access should not be masked (unsafe), false otherwise.</param>
|
||||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||||
public MemoryManagerHostMapped(ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null)
|
public MemoryManagerHostMapped(MemoryBlock backingMemory, ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null)
|
||||||
{
|
{
|
||||||
|
_backingMemory = backingMemory;
|
||||||
|
_pageTable = new PageTable<ulong>();
|
||||||
_invalidAccessHandler = invalidAccessHandler;
|
_invalidAccessHandler = invalidAccessHandler;
|
||||||
_unsafeMode = unsafeMode;
|
_unsafeMode = unsafeMode;
|
||||||
_addressSpaceSize = addressSpaceSize;
|
_addressSpaceSize = addressSpaceSize;
|
||||||
@@ -79,9 +84,13 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
AddressSpaceBits = asBits;
|
AddressSpaceBits = asBits;
|
||||||
|
|
||||||
_pageTable = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))];
|
_pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))];
|
||||||
_addressSpace = new MemoryBlock(asSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable);
|
|
||||||
_addressSpaceMirror = _addressSpace.CreateMirror();
|
MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
|
||||||
|
|
||||||
|
_addressSpace = new MemoryBlock(asSize, asFlags);
|
||||||
|
_addressSpaceMirror = new MemoryBlock(asSize, asFlags | MemoryAllocationFlags.ForceWindows4KBViewMapping);
|
||||||
|
|
||||||
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
|
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
|
||||||
_memoryEh = new MemoryEhMeilleure(_addressSpace, Tracking);
|
_memoryEh = new MemoryEhMeilleure(_addressSpace, Tracking);
|
||||||
}
|
}
|
||||||
@@ -136,12 +145,14 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Map(ulong va, nuint hostAddress, ulong size)
|
public void Map(ulong va, ulong pa, ulong size)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
_addressSpace.Commit(va, size);
|
_addressSpace.MapView(_backingMemory, pa, va, size);
|
||||||
|
_addressSpaceMirror.MapView(_backingMemory, pa, va, size);
|
||||||
AddMapping(va, size);
|
AddMapping(va, size);
|
||||||
|
PtMap(va, pa, size);
|
||||||
|
|
||||||
Tracking.Map(va, size);
|
Tracking.Map(va, size);
|
||||||
}
|
}
|
||||||
@@ -155,7 +166,32 @@ namespace Ryujinx.Cpu
|
|||||||
Tracking.Unmap(va, size);
|
Tracking.Unmap(va, size);
|
||||||
|
|
||||||
RemoveMapping(va, size);
|
RemoveMapping(va, size);
|
||||||
_addressSpace.Decommit(va, size);
|
PtUnmap(va, size);
|
||||||
|
_addressSpace.UnmapView(_backingMemory, va, size);
|
||||||
|
_addressSpaceMirror.UnmapView(_backingMemory, va, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PtMap(ulong va, ulong pa, ulong size)
|
||||||
|
{
|
||||||
|
while (size != 0)
|
||||||
|
{
|
||||||
|
_pageTable.Map(va, pa);
|
||||||
|
|
||||||
|
va += PageSize;
|
||||||
|
pa += PageSize;
|
||||||
|
size -= PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PtUnmap(ulong va, ulong size)
|
||||||
|
{
|
||||||
|
while (size != 0)
|
||||||
|
{
|
||||||
|
_pageTable.Unmap(va);
|
||||||
|
|
||||||
|
va += PageSize;
|
||||||
|
size -= PageSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -216,6 +252,7 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Write<T>(ulong va, T value) where T : unmanaged
|
public void Write<T>(ulong va, T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
@@ -267,7 +304,7 @@ namespace Ryujinx.Cpu
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
||||||
@@ -322,7 +359,7 @@ namespace Ryujinx.Cpu
|
|||||||
int bit = (int)((page & 31) << 1);
|
int bit = (int)((page & 31) << 1);
|
||||||
|
|
||||||
int pageIndex = (int)(page >> PageToPteShift);
|
int pageIndex = (int)(page >> PageToPteShift);
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex];
|
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||||
|
|
||||||
ulong pte = Volatile.Read(ref pageRef);
|
ulong pte = Volatile.Read(ref pageRef);
|
||||||
|
|
||||||
@@ -373,7 +410,7 @@ namespace Ryujinx.Cpu
|
|||||||
mask &= endMask;
|
mask &= endMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex++];
|
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||||
ulong pte = Volatile.Read(ref pageRef);
|
ulong pte = Volatile.Read(ref pageRef);
|
||||||
|
|
||||||
pte |= pte >> 1;
|
pte |= pte >> 1;
|
||||||
@@ -389,16 +426,53 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
|
ulong regionStart = GetPhysicalAddressChecked(va);
|
||||||
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
|
for (int page = 0; page < pages - 1; page++)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<HostMemoryRange>();
|
if (!ValidateAddress(va + PageSize))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssertMapped(va, size);
|
ulong newPa = GetPhysicalAddressChecked(va + PageSize);
|
||||||
|
|
||||||
return new HostMemoryRange[] { new HostMemoryRange(_addressSpaceMirror.GetPointer(va, size), size) };
|
if (GetPhysicalAddressChecked(va) + PageSize != newPa)
|
||||||
|
{
|
||||||
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
|
regionStart = newPa;
|
||||||
|
regionSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
va += PageSize;
|
||||||
|
regionSize += PageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong GetPhysicalAddressChecked(ulong va)
|
||||||
|
{
|
||||||
|
if (!IsMapped(va))
|
||||||
|
{
|
||||||
|
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetPhysicalAddressInternal(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong GetPhysicalAddressInternal(ulong va)
|
||||||
|
{
|
||||||
|
return _pageTable.Read(va) + (va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -427,7 +501,7 @@ namespace Ryujinx.Cpu
|
|||||||
int bit = (int)((pageStart & 31) << 1);
|
int bit = (int)((pageStart & 31) << 1);
|
||||||
|
|
||||||
int pageIndex = (int)(pageStart >> PageToPteShift);
|
int pageIndex = (int)(pageStart >> PageToPteShift);
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex];
|
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||||
|
|
||||||
ulong pte = Volatile.Read(ref pageRef);
|
ulong pte = Volatile.Read(ref pageRef);
|
||||||
ulong state = ((pte >> bit) & 3);
|
ulong state = ((pte >> bit) & 3);
|
||||||
@@ -459,7 +533,7 @@ namespace Ryujinx.Cpu
|
|||||||
mask &= endMask;
|
mask &= endMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex++];
|
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||||
|
|
||||||
ulong pte = Volatile.Read(ref pageRef);
|
ulong pte = Volatile.Read(ref pageRef);
|
||||||
ulong mappedMask = mask & BlockMappedMask;
|
ulong mappedMask = mask & BlockMappedMask;
|
||||||
@@ -530,7 +604,7 @@ namespace Ryujinx.Cpu
|
|||||||
ulong tag = protTag << bit;
|
ulong tag = protTag << bit;
|
||||||
|
|
||||||
int pageIndex = (int)(pageStart >> PageToPteShift);
|
int pageIndex = (int)(pageStart >> PageToPteShift);
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex];
|
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||||
|
|
||||||
ulong pte;
|
ulong pte;
|
||||||
|
|
||||||
@@ -562,7 +636,7 @@ namespace Ryujinx.Cpu
|
|||||||
mask &= endMask;
|
mask &= endMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex++];
|
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||||
|
|
||||||
ulong pte;
|
ulong pte;
|
||||||
ulong mappedMask;
|
ulong mappedMask;
|
||||||
@@ -632,7 +706,7 @@ namespace Ryujinx.Cpu
|
|||||||
mask &= endMask;
|
mask &= endMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex++];
|
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||||
|
|
||||||
ulong pte;
|
ulong pte;
|
||||||
ulong mappedMask;
|
ulong mappedMask;
|
||||||
@@ -677,7 +751,7 @@ namespace Ryujinx.Cpu
|
|||||||
mask |= endMask;
|
mask |= endMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref ulong pageRef = ref _pageTable[pageIndex++];
|
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||||
ulong pte;
|
ulong pte;
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -695,11 +769,11 @@ namespace Ryujinx.Cpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
_addressSpaceMirror.Dispose();
|
|
||||||
_addressSpace.Dispose();
|
_addressSpace.Dispose();
|
||||||
|
_addressSpaceMirror.Dispose();
|
||||||
_memoryEh.Dispose();
|
_memoryEh.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
|
private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
|
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
|
||||||
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
|
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
|
||||||
|
|
||||||
void SetViewports(int first, ReadOnlySpan<Viewport> viewports);
|
void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform);
|
||||||
|
|
||||||
void TextureBarrier();
|
void TextureBarrier();
|
||||||
void TextureBarrierTiled();
|
void TextureBarrierTiled();
|
||||||
|
@@ -9,17 +9,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
public CommandType CommandType => CommandType.SetViewports;
|
public CommandType CommandType => CommandType.SetViewports;
|
||||||
private int _first;
|
private int _first;
|
||||||
private SpanRef<Viewport> _viewports;
|
private SpanRef<Viewport> _viewports;
|
||||||
|
private bool _disableTransform;
|
||||||
|
|
||||||
public void Set(int first, SpanRef<Viewport> viewports)
|
public void Set(int first, SpanRef<Viewport> viewports, bool disableTransform)
|
||||||
{
|
{
|
||||||
_first = first;
|
_first = first;
|
||||||
_viewports = viewports;
|
_viewports = viewports;
|
||||||
|
_disableTransform = disableTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded);
|
ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded);
|
||||||
renderer.Pipeline.SetViewports(command._first, viewports);
|
renderer.Pipeline.SetViewports(command._first, viewports, command._disableTransform);
|
||||||
command._viewports.Dispose(threaded);
|
command._viewports.Dispose(threaded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -304,9 +304,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports)
|
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform)
|
||||||
{
|
{
|
||||||
_renderer.New<SetViewportsCommand>().Set(first, _renderer.CopySpan(viewports));
|
_renderer.New<SetViewportsCommand>().Set(first, _renderer.CopySpan(viewports), disableTransform);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,15 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
DecrementAndClamp,
|
DecrementAndClamp,
|
||||||
Invert,
|
Invert,
|
||||||
IncrementAndWrap,
|
IncrementAndWrap,
|
||||||
DecrementAndWrap
|
DecrementAndWrap,
|
||||||
|
|
||||||
|
ZeroGl = 0x0,
|
||||||
|
InvertGl = 0x150a,
|
||||||
|
KeepGl = 0x1e00,
|
||||||
|
ReplaceGl = 0x1e01,
|
||||||
|
IncrementAndClampGl = 0x1e02,
|
||||||
|
DecrementAndClampGl = 0x1e03,
|
||||||
|
IncrementAndWrapGl = 0x8507,
|
||||||
|
DecrementAndWrapGl = 0x8508
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -72,6 +72,13 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count);
|
UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateViewportInverse(Vector4<float> data)
|
||||||
|
{
|
||||||
|
Data.ViewportInverse = data;
|
||||||
|
|
||||||
|
MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize);
|
||||||
|
}
|
||||||
|
|
||||||
public void Commit()
|
public void Commit()
|
||||||
{
|
{
|
||||||
if (_startOffset != -1)
|
if (_startOffset != -1)
|
||||||
|
@@ -113,7 +113,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
nameof(ThreedClassState.DepthMode),
|
nameof(ThreedClassState.DepthMode),
|
||||||
nameof(ThreedClassState.ViewportTransform),
|
nameof(ThreedClassState.ViewportTransform),
|
||||||
nameof(ThreedClassState.ViewportExtents),
|
nameof(ThreedClassState.ViewportExtents),
|
||||||
nameof(ThreedClassState.YControl)),
|
nameof(ThreedClassState.YControl),
|
||||||
|
nameof(ThreedClassState.ViewportTransformEnable)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdatePolygonMode,
|
new StateUpdateCallbackEntry(UpdatePolygonMode,
|
||||||
nameof(ThreedClassState.PolygonModeFront),
|
nameof(ThreedClassState.PolygonModeFront),
|
||||||
@@ -200,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
// of the shader for the new state.
|
// of the shader for the new state.
|
||||||
if (_shaderSpecState != null)
|
if (_shaderSpecState != null)
|
||||||
{
|
{
|
||||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState()))
|
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState()))
|
||||||
{
|
{
|
||||||
ForceShaderUpdate();
|
ForceShaderUpdate();
|
||||||
}
|
}
|
||||||
@@ -568,6 +569,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
var yControl = _state.State.YControl;
|
var yControl = _state.State.YControl;
|
||||||
var face = _state.State.FaceState;
|
var face = _state.State.FaceState;
|
||||||
|
|
||||||
|
bool disableTransform = _state.State.ViewportTransformEnable == 0;
|
||||||
|
|
||||||
UpdateFrontFace(yControl, face.FrontFace);
|
UpdateFrontFace(yControl, face.FrontFace);
|
||||||
UpdateDepthMode();
|
UpdateDepthMode();
|
||||||
|
|
||||||
@@ -577,6 +580,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||||
{
|
{
|
||||||
|
if (disableTransform)
|
||||||
|
{
|
||||||
|
ref var scissor = ref _state.State.ScreenScissorState;
|
||||||
|
|
||||||
|
float rScale = _channel.TextureManager.RenderTargetScale;
|
||||||
|
var scissorRect = new RectangleF(0, 0, (scissor.X + scissor.Width) * rScale, (scissor.Y + scissor.Height) * rScale);
|
||||||
|
|
||||||
|
viewports[index] = new Viewport(scissorRect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, ViewportSwizzle.PositiveZ, ViewportSwizzle.PositiveW, 0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ref var transform = ref _state.State.ViewportTransform[index];
|
ref var transform = ref _state.State.ViewportTransform[index];
|
||||||
ref var extents = ref _state.State.ViewportExtents[index];
|
ref var extents = ref _state.State.ViewportExtents[index];
|
||||||
|
|
||||||
@@ -628,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
|
viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetViewports(0, viewports);
|
_context.Renderer.Pipeline.SetViewports(0, viewports, disableTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -928,7 +942,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
size = endAddress.Pack() - address + 1;
|
size = endAddress.Pack() - address + 1;
|
||||||
|
|
||||||
if (stride > 0 && indexTypeSmall)
|
if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced)
|
||||||
{
|
{
|
||||||
// If the index type is a small integer type, then we might be still able
|
// If the index type is a small integer type, then we might be still able
|
||||||
// to reduce the vertex buffer size based on the maximum possible index value.
|
// to reduce the vertex buffer size based on the maximum possible index value.
|
||||||
@@ -1194,7 +1208,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return new GpuChannelGraphicsState(
|
return new GpuChannelGraphicsState(
|
||||||
_state.State.EarlyZForce,
|
_state.State.EarlyZForce,
|
||||||
_drawState.Topology,
|
_drawState.Topology,
|
||||||
_state.State.TessMode);
|
_state.State.TessMode,
|
||||||
|
_state.State.ViewportTransformEnable == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -1393,6 +1393,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="size">The size of the flushing memory access</param>
|
/// <param name="size">The size of the flushing memory access</param>
|
||||||
public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
|
public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
// There is a small gap here where the action is removed but _actionRegistered is still 1.
|
||||||
|
// In this case it will skip registering the action, but here we are already handling it,
|
||||||
|
// so there shouldn't be any issue as it's the same handler for all actions.
|
||||||
|
|
||||||
|
handle.ClearActionRegistered();
|
||||||
|
|
||||||
if (!handle.Modified)
|
if (!handle.Modified)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@@ -32,9 +33,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private ulong _modifiedSync;
|
private ulong _modifiedSync;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a tracking action is currently registered or not.
|
/// Whether a tracking action is currently registered or not. (0/1)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _actionRegistered;
|
private int _actionRegistered;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a sync action is currently registered or not.
|
/// Whether a sync action is currently registered or not.
|
||||||
@@ -171,11 +172,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_syncActionRegistered = true;
|
_syncActionRegistered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_actionRegistered)
|
if (Interlocked.Exchange(ref _actionRegistered, 1) == 0)
|
||||||
{
|
{
|
||||||
_group.RegisterAction(this);
|
_group.RegisterAction(this);
|
||||||
|
|
||||||
_actionRegistered = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,8 +232,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="context">The GPU context used to wait for sync</param>
|
/// <param name="context">The GPU context used to wait for sync</param>
|
||||||
public void Sync(GpuContext context)
|
public void Sync(GpuContext context)
|
||||||
{
|
{
|
||||||
_actionRegistered = false;
|
|
||||||
|
|
||||||
bool needsSync = !context.IsGpuThread();
|
bool needsSync = !context.IsGpuThread();
|
||||||
|
|
||||||
if (needsSync)
|
if (needsSync)
|
||||||
@@ -263,21 +260,39 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the action registered variable, indicating that the tracking action should be
|
||||||
|
/// re-registered on the next modification.
|
||||||
|
/// </summary>
|
||||||
|
public void ClearActionRegistered()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _actionRegistered, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action to perform when a sync number is registered after modification.
|
/// Action to perform when a sync number is registered after modification.
|
||||||
/// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen.
|
/// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void SyncAction()
|
private void SyncAction()
|
||||||
{
|
{
|
||||||
|
// The storage will need to signal modified again to update the sync number in future.
|
||||||
|
_group.Storage.SignalModifiedDirty();
|
||||||
|
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
foreach (Texture texture in Overlaps)
|
||||||
|
{
|
||||||
|
texture.SignalModifiedDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register region tracking for CPU? (again)
|
// Register region tracking for CPU? (again)
|
||||||
_registeredSync = _modifiedSync;
|
_registeredSync = _modifiedSync;
|
||||||
_syncActionRegistered = false;
|
_syncActionRegistered = false;
|
||||||
|
|
||||||
if (!_actionRegistered)
|
if (Interlocked.Exchange(ref _actionRegistered, 1) == 0)
|
||||||
{
|
{
|
||||||
_group.RegisterAction(this);
|
_group.RegisterAction(this);
|
||||||
|
|
||||||
_actionRegistered = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -166,7 +166,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
|||||||
GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
|
GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
|
||||||
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
|
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
|
||||||
topology,
|
topology,
|
||||||
tessMode);
|
tessMode,
|
||||||
|
false);
|
||||||
|
|
||||||
TransformFeedbackDescriptor[] tfdNew = null;
|
TransformFeedbackDescriptor[] tfdNew = null;
|
||||||
|
|
||||||
|
@@ -185,6 +185,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return _oldSpecState.GraphicsState.EarlyZForce;
|
return _oldSpecState.GraphicsState.EarlyZForce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryViewportTransformDisable()
|
||||||
|
{
|
||||||
|
return _oldSpecState.GraphicsState.ViewportTransformDisable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RegisterTexture(int handle, int cbufSlot)
|
public void RegisterTexture(int handle, int cbufSlot)
|
||||||
{
|
{
|
||||||
|
@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 1;
|
private const ushort FileFormatVersionMinor = 1;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 0;
|
private const uint CodeGenVersion = 1;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -217,6 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return _state.GraphicsState.EarlyZForce;
|
return _state.GraphicsState.EarlyZForce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryViewportTransformDisable()
|
||||||
|
{
|
||||||
|
return _state.GraphicsState.ViewportTransformDisable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RegisterTexture(int handle, int cbufSlot)
|
public void RegisterTexture(int handle, int cbufSlot)
|
||||||
{
|
{
|
||||||
|
@@ -25,17 +25,24 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TessMode TessellationMode;
|
public readonly TessMode TessellationMode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whenever the viewport transform is disabled.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool ViewportTransformDisable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU graphics state.
|
/// Creates a new GPU graphics state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="earlyZForce">Early Z force enable</param>
|
/// <param name="earlyZForce">Early Z force enable</param>
|
||||||
/// <param name="topology">Primitive topology</param>
|
/// <param name="topology">Primitive topology</param>
|
||||||
/// <param name="tessellationMode">Tessellation mode</param>
|
/// <param name="tessellationMode">Tessellation mode</param>
|
||||||
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode)
|
/// <param name="viewportTransformDisable">Indicates whenever the viewport transform is disabled</param>
|
||||||
|
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable)
|
||||||
{
|
{
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
TessellationMode = tessellationMode;
|
TessellationMode = tessellationMode;
|
||||||
|
ViewportTransformDisable = viewportTransformDisable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -249,12 +249,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
GpuChannelGraphicsState graphicsState,
|
GpuChannelGraphicsState graphicsState,
|
||||||
ShaderAddresses addresses)
|
ShaderAddresses addresses)
|
||||||
{
|
{
|
||||||
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, gpShaders, addresses))
|
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses))
|
||||||
{
|
{
|
||||||
return gpShaders;
|
return gpShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_graphicsShaderCache.TryFind(channel, poolState, addresses, out gpShaders, out var cachedGuestCode))
|
if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode))
|
||||||
{
|
{
|
||||||
_gpPrograms[addresses] = gpShaders;
|
_gpPrograms[addresses] = gpShaders;
|
||||||
return gpShaders;
|
return gpShaders;
|
||||||
@@ -429,12 +429,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel using the shader</param>
|
/// <param name="channel">GPU channel using the shader</param>
|
||||||
/// <param name="poolState">GPU channel state to verify shader compatibility</param>
|
/// <param name="poolState">GPU channel state to verify shader compatibility</param>
|
||||||
|
/// <param name="graphicsState">GPU channel graphics state to verify shader compatibility</param>
|
||||||
/// <param name="gpShaders">Cached graphics shaders</param>
|
/// <param name="gpShaders">Cached graphics shaders</param>
|
||||||
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
|
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
|
||||||
/// <returns>True if the code is different, false otherwise</returns>
|
/// <returns>True if the code is different, false otherwise</returns>
|
||||||
private static bool IsShaderEqual(
|
private static bool IsShaderEqual(
|
||||||
GpuChannel channel,
|
GpuChannel channel,
|
||||||
GpuChannelPoolState poolState,
|
GpuChannelPoolState poolState,
|
||||||
|
GpuChannelGraphicsState graphicsState,
|
||||||
CachedShaderProgram gpShaders,
|
CachedShaderProgram gpShaders,
|
||||||
ShaderAddresses addresses)
|
ShaderAddresses addresses)
|
||||||
{
|
{
|
||||||
@@ -452,7 +454,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState);
|
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -208,6 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="graphicsState">Graphics state</param>
|
||||||
/// <param name="addresses">Guest addresses of the shaders to find</param>
|
/// <param name="addresses">Guest addresses of the shaders to find</param>
|
||||||
/// <param name="program">Cached host program for the given state, if found</param>
|
/// <param name="program">Cached host program for the given state, if found</param>
|
||||||
/// <param name="guestCode">Cached guest code, if any found</param>
|
/// <param name="guestCode">Cached guest code, if any found</param>
|
||||||
@@ -215,6 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
public bool TryFind(
|
public bool TryFind(
|
||||||
GpuChannel channel,
|
GpuChannel channel,
|
||||||
GpuChannelPoolState poolState,
|
GpuChannelPoolState poolState,
|
||||||
|
GpuChannelGraphicsState graphicsState,
|
||||||
ShaderAddresses addresses,
|
ShaderAddresses addresses,
|
||||||
out CachedShaderProgram program,
|
out CachedShaderProgram program,
|
||||||
out CachedGraphicsGuestCode guestCode)
|
out CachedGraphicsGuestCode guestCode)
|
||||||
@@ -234,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
|
if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
|
||||||
{
|
{
|
||||||
return specList.TryFindForGraphics(channel, poolState, out program);
|
return specList.TryFindForGraphics(channel, poolState, graphicsState, out program);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -24,13 +24,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="graphicsState">Graphics state</param>
|
||||||
/// <param name="program">Cached program, if found</param>
|
/// <param name="program">Cached program, if found</param>
|
||||||
/// <returns>True if a compatible program is found, false otherwise</returns>
|
/// <returns>True if a compatible program is found, false otherwise</returns>
|
||||||
public bool TryFindForGraphics(GpuChannel channel, GpuChannelPoolState poolState, out CachedShaderProgram program)
|
public bool TryFindForGraphics(
|
||||||
|
GpuChannel channel,
|
||||||
|
GpuChannelPoolState poolState,
|
||||||
|
GpuChannelGraphicsState graphicsState,
|
||||||
|
out CachedShaderProgram program)
|
||||||
{
|
{
|
||||||
foreach (var entry in _entries)
|
foreach (var entry in _entries)
|
||||||
{
|
{
|
||||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState))
|
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
|
||||||
{
|
{
|
||||||
program = entry;
|
program = entry;
|
||||||
return true;
|
return true;
|
||||||
|
@@ -395,9 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="graphicsState">Graphics state</param>
|
||||||
/// <returns>True if the state matches, false otherwise</returns>
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState)
|
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState)
|
||||||
{
|
{
|
||||||
|
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Matches(channel, poolState, isCompute: false);
|
return Matches(channel, poolState, isCompute: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -379,20 +379,28 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case GAL.StencilOp.Keep:
|
case GAL.StencilOp.Keep:
|
||||||
|
case GAL.StencilOp.KeepGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Keep;
|
return OpenTK.Graphics.OpenGL.StencilOp.Keep;
|
||||||
case GAL.StencilOp.Zero:
|
case GAL.StencilOp.Zero:
|
||||||
|
case GAL.StencilOp.ZeroGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Zero;
|
return OpenTK.Graphics.OpenGL.StencilOp.Zero;
|
||||||
case GAL.StencilOp.Replace:
|
case GAL.StencilOp.Replace:
|
||||||
|
case GAL.StencilOp.ReplaceGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Replace;
|
return OpenTK.Graphics.OpenGL.StencilOp.Replace;
|
||||||
case GAL.StencilOp.IncrementAndClamp:
|
case GAL.StencilOp.IncrementAndClamp:
|
||||||
|
case GAL.StencilOp.IncrementAndClampGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Incr;
|
return OpenTK.Graphics.OpenGL.StencilOp.Incr;
|
||||||
case GAL.StencilOp.DecrementAndClamp:
|
case GAL.StencilOp.DecrementAndClamp:
|
||||||
|
case GAL.StencilOp.DecrementAndClampGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Decr;
|
return OpenTK.Graphics.OpenGL.StencilOp.Decr;
|
||||||
case GAL.StencilOp.Invert:
|
case GAL.StencilOp.Invert:
|
||||||
|
case GAL.StencilOp.InvertGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.Invert;
|
return OpenTK.Graphics.OpenGL.StencilOp.Invert;
|
||||||
case GAL.StencilOp.IncrementAndWrap:
|
case GAL.StencilOp.IncrementAndWrap:
|
||||||
|
case GAL.StencilOp.IncrementAndWrapGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.IncrWrap;
|
return OpenTK.Graphics.OpenGL.StencilOp.IncrWrap;
|
||||||
case GAL.StencilOp.DecrementAndWrap:
|
case GAL.StencilOp.DecrementAndWrap:
|
||||||
|
case GAL.StencilOp.DecrementAndWrapGl:
|
||||||
return OpenTK.Graphics.OpenGL.StencilOp.DecrWrap;
|
return OpenTK.Graphics.OpenGL.StencilOp.DecrWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1266,7 +1266,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexArray.SetVertexBuffers(vertexBuffers);
|
_vertexArray.SetVertexBuffers(vertexBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports)
|
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _viewportArray, viewports.Length * 4);
|
Array.Resize(ref _viewportArray, viewports.Length * 4);
|
||||||
Array.Resize(ref _depthRangeArray, viewports.Length * 2);
|
Array.Resize(ref _depthRangeArray, viewports.Length * 2);
|
||||||
@@ -1305,6 +1305,19 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
GL.ViewportArray(first, viewports.Length, viewportArray);
|
GL.ViewportArray(first, viewports.Length, viewportArray);
|
||||||
GL.DepthRangeArray(first, viewports.Length, depthRangeArray);
|
GL.DepthRangeArray(first, viewports.Length, depthRangeArray);
|
||||||
|
|
||||||
|
float disableTransformF = disableTransform ? 1.0f : 0.0f;
|
||||||
|
if (_supportBuffer.Data.ViewportInverse.W != disableTransformF || disableTransform)
|
||||||
|
{
|
||||||
|
float scale = _renderScale[0].X;
|
||||||
|
_supportBuffer.UpdateViewportInverse(new Vector4<float>
|
||||||
|
{
|
||||||
|
X = scale * 2f / viewports[first].Region.Width,
|
||||||
|
Y = scale * 2f / viewports[first].Region.Height,
|
||||||
|
Z = 1,
|
||||||
|
W = disableTransformF
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TextureBarrier()
|
public void TextureBarrier()
|
||||||
|
@@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isFragment)
|
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
|
||||||
{
|
{
|
||||||
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
|
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
|
||||||
}
|
}
|
||||||
@@ -615,8 +615,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
|
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
|
||||||
{
|
{
|
||||||
bool isFragment = stage == ShaderStage.Fragment;
|
bool needsSupportBlock = stage == ShaderStage.Fragment ||
|
||||||
if (!isFragment && scaleElements == 0)
|
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
|
||||||
|
|
||||||
|
if (!needsSupportBlock && scaleElements == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -630,6 +632,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
case ShaderStage.Vertex:
|
case ShaderStage.Vertex:
|
||||||
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
|
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
|
||||||
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
|
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
|
||||||
|
context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
|
||||||
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
|
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
|
||||||
break;
|
break;
|
||||||
case ShaderStage.Compute:
|
case ShaderStage.Compute:
|
||||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
public const string SupportBlockName = "support_block";
|
public const string SupportBlockName = "support_block";
|
||||||
public const string SupportBlockAlphaTestName = "s_alpha_test";
|
public const string SupportBlockAlphaTestName = "s_alpha_test";
|
||||||
public const string SupportBlockIsBgraName = "s_is_bgra";
|
public const string SupportBlockIsBgraName = "s_is_bgra";
|
||||||
|
public const string SupportBlockViewportInverse = "s_viewport_inverse";
|
||||||
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
|
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
|
||||||
public const string SupportBlockRenderScaleName = "s_render_scale";
|
public const string SupportBlockRenderScaleName = "s_render_scale";
|
||||||
|
|
||||||
|
@@ -84,7 +84,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
|
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) },
|
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) },
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) },
|
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) },
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) }
|
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) },
|
||||||
|
|
||||||
|
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) },
|
||||||
|
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) }
|
||||||
};
|
};
|
||||||
|
|
||||||
private Dictionary<AstOperand, string> _locals;
|
private Dictionary<AstOperand, string> _locals;
|
||||||
|
@@ -329,6 +329,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries if host state disables the viewport transform.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the viewport transform is disabled</returns>
|
||||||
|
bool QueryViewportTransformDisable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a texture used by the shader.
|
/// Registers a texture used by the shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -206,7 +206,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (emit)
|
if (emit)
|
||||||
{
|
{
|
||||||
|
if (context.Config.LastInVertexPipeline)
|
||||||
|
{
|
||||||
|
context.PrepareForVertexReturn(out var tempXLocal, out var tempYLocal, out var tempZLocal);
|
||||||
|
|
||||||
context.EmitVertex();
|
context.EmitVertex();
|
||||||
|
|
||||||
|
// Restore output position value before transformation.
|
||||||
|
|
||||||
|
if (tempXLocal != null)
|
||||||
|
{
|
||||||
|
context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempYLocal != null)
|
||||||
|
{
|
||||||
|
context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempZLocal != null)
|
||||||
|
{
|
||||||
|
context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.EmitVertex();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cut)
|
if (cut)
|
||||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
|
|
||||||
public static int FragmentAlphaTestOffset;
|
public static int FragmentAlphaTestOffset;
|
||||||
public static int FragmentIsBgraOffset;
|
public static int FragmentIsBgraOffset;
|
||||||
|
public static int ViewportInverseOffset;
|
||||||
public static int FragmentRenderScaleCountOffset;
|
public static int FragmentRenderScaleCountOffset;
|
||||||
public static int GraphicsRenderScaleOffset;
|
public static int GraphicsRenderScaleOffset;
|
||||||
public static int ComputeRenderScaleOffset;
|
public static int ComputeRenderScaleOffset;
|
||||||
@@ -40,6 +41,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
|
|
||||||
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
|
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
|
||||||
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
|
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
|
||||||
|
ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse);
|
||||||
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
|
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
|
||||||
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
|
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
|
||||||
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
||||||
@@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
|
|
||||||
public Vector4<int> FragmentAlphaTest;
|
public Vector4<int> FragmentAlphaTest;
|
||||||
public Array8<Vector4<int>> FragmentIsBgra;
|
public Array8<Vector4<int>> FragmentIsBgra;
|
||||||
|
public Vector4<float> ViewportInverse;
|
||||||
public Vector4<int> FragmentRenderScaleCount;
|
public Vector4<int> FragmentRenderScaleCount;
|
||||||
|
|
||||||
// Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs.
|
// Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs.
|
||||||
|
@@ -67,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public const int FragmentOutputIsBgraBase = 0x1000100;
|
public const int FragmentOutputIsBgraBase = 0x1000100;
|
||||||
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
|
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
|
||||||
|
|
||||||
|
public const int SupportBlockViewInverseX = 0x1000200;
|
||||||
|
public const int SupportBlockViewInverseY = 0x1000204;
|
||||||
|
|
||||||
public const int ThreadIdX = 0x2000000;
|
public const int ThreadIdX = 0x2000000;
|
||||||
public const int ThreadIdY = 0x2000004;
|
public const int ThreadIdY = 0x2000004;
|
||||||
public const int ThreadIdZ = 0x2000008;
|
public const int ThreadIdZ = 0x2000008;
|
||||||
|
@@ -154,9 +154,56 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PrepareForVertexReturn()
|
||||||
|
{
|
||||||
|
if (Config.GpuAccessor.QueryViewportTransformDisable())
|
||||||
|
{
|
||||||
|
Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask);
|
||||||
|
Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask);
|
||||||
|
Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX);
|
||||||
|
Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY);
|
||||||
|
Operand negativeOne = ConstF(-1.0f);
|
||||||
|
|
||||||
|
this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
|
||||||
|
this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
|
||||||
|
{
|
||||||
|
if (Config.GpuAccessor.QueryViewportTransformDisable())
|
||||||
|
{
|
||||||
|
oldXLocal = Local();
|
||||||
|
this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
|
||||||
|
oldYLocal = Local();
|
||||||
|
this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oldXLocal = null;
|
||||||
|
oldYLocal = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will be used by Vulkan backend for depth mode emulation.
|
||||||
|
oldZLocal = null;
|
||||||
|
|
||||||
|
PrepareForVertexReturn();
|
||||||
|
}
|
||||||
|
|
||||||
public void PrepareForReturn()
|
public void PrepareForReturn()
|
||||||
{
|
{
|
||||||
if (!IsNonMain && Config.Stage == ShaderStage.Fragment)
|
if (IsNonMain)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.LastInVertexPipeline &&
|
||||||
|
(Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) &&
|
||||||
|
(Config.Options.Flags & TranslationFlags.VertexA) == 0)
|
||||||
|
{
|
||||||
|
PrepareForVertexReturn();
|
||||||
|
}
|
||||||
|
else if (Config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
if (Config.OmapDepth)
|
if (Config.OmapDepth)
|
||||||
{
|
{
|
||||||
|
@@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public ShaderStage Stage { get; }
|
public ShaderStage Stage { get; }
|
||||||
|
|
||||||
public bool GpPassthrough { get; }
|
public bool GpPassthrough { get; }
|
||||||
|
public bool LastInVertexPipeline { get; private set; }
|
||||||
|
|
||||||
public int ThreadsPerInputPrimitive { get; }
|
public int ThreadsPerInputPrimitive { get; }
|
||||||
|
|
||||||
@@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
OmapSampleMask = header.OmapSampleMask;
|
OmapSampleMask = header.OmapSampleMask;
|
||||||
OmapDepth = header.OmapDepth;
|
OmapDepth = header.OmapDepth;
|
||||||
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||||
|
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDepthRegister()
|
public int GetDepthRegister()
|
||||||
@@ -274,6 +276,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
|
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
|
||||||
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
||||||
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
||||||
|
|
||||||
|
if (config.Stage != ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
LastInVertexPipeline = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
||||||
|
@@ -17,12 +17,14 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||||
using static Ryujinx.HLE.HOS.ModLoader;
|
using static Ryujinx.HLE.HOS.ModLoader;
|
||||||
@@ -307,6 +309,94 @@ namespace Ryujinx.HLE.HOS
|
|||||||
LoadNca(nca, null, null);
|
LoadNca(nca, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadServiceNca(string ncaFile)
|
||||||
|
{
|
||||||
|
// Disable PPTC here as it does not support multiple processes running.
|
||||||
|
// TODO: This should be eventually removed and it should stop using global state and
|
||||||
|
// instead manage the cache per process.
|
||||||
|
Ptc.Close();
|
||||||
|
PtcProfiler.Stop();
|
||||||
|
|
||||||
|
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||||
|
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||||
|
|
||||||
|
if (mainNca.Header.ContentType != NcaContentType.Program)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IFileSystem codeFs = null;
|
||||||
|
|
||||||
|
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
||||||
|
{
|
||||||
|
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeFs == null)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var npdmFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
|
Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||||
|
|
||||||
|
MetaLoader metaData;
|
||||||
|
|
||||||
|
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
|
||||||
|
|
||||||
|
var npdmBuffer = new byte[fileSize];
|
||||||
|
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
|
||||||
|
|
||||||
|
metaData = new MetaLoader();
|
||||||
|
metaData.Load(npdmBuffer).ThrowIfFailure();
|
||||||
|
|
||||||
|
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < nsos.Length; i++)
|
||||||
|
{
|
||||||
|
string name = ExeFsPrefixes[i];
|
||||||
|
|
||||||
|
if (!codeFs.FileExists($"/{name}"))
|
||||||
|
{
|
||||||
|
continue; // File doesn't exist, skip.
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
||||||
|
|
||||||
|
using var nsoFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
|
codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
|
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the nsos, ignoring ones that aren't used.
|
||||||
|
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
||||||
|
|
||||||
|
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
|
||||||
|
|
||||||
|
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||||
|
{
|
||||||
|
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||||
|
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
|
||||||
|
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
|
||||||
|
|
||||||
|
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
|
||||||
|
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
||||||
|
|
||||||
|
string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0');
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
|
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
|
||||||
{
|
{
|
||||||
if (mainNca.Header.ContentType != NcaContentType.Program)
|
if (mainNca.Header.ContentType != NcaContentType.Program)
|
||||||
@@ -507,7 +597,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
||||||
{
|
{
|
||||||
metaData = null; //TODO: Check if we should retain old npdm
|
metaData = null; // TODO: Check if we should retain old npdm.
|
||||||
}
|
}
|
||||||
|
|
||||||
metaData ??= ReadNpdm(codeFs);
|
metaData ??= ReadNpdm(codeFs);
|
||||||
@@ -520,7 +610,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (!codeFs.FileExists($"/{name}"))
|
if (!codeFs.FileExists($"/{name}"))
|
||||||
{
|
{
|
||||||
continue; // file doesn't exist, skip
|
continue; // File doesn't exist, skip.
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
||||||
@@ -532,13 +622,13 @@ namespace Ryujinx.HLE.HOS
|
|||||||
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExeFs file replacements
|
// ExeFs file replacements.
|
||||||
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
|
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
|
||||||
|
|
||||||
// collect the nsos, ignoring ones that aren't used
|
// Collect the nsos, ignoring ones that aren't used.
|
||||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
||||||
|
|
||||||
// take the npdm from mods if present
|
// Take the npdm from mods if present.
|
||||||
if (modLoadResult.Npdm != null)
|
if (modLoadResult.Npdm != null)
|
||||||
{
|
{
|
||||||
metaData = modLoadResult.Npdm;
|
metaData = modLoadResult.Npdm;
|
||||||
@@ -561,10 +651,21 @@ namespace Ryujinx.HLE.HOS
|
|||||||
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
||||||
_device.Gpu.HostInitalized.Set();
|
_device.Gpu.HostInitalized.Set();
|
||||||
|
|
||||||
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, _device.Configuration.MemoryManagerMode);
|
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
|
||||||
|
|
||||||
|
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||||
|
{
|
||||||
|
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
|
||||||
|
|
||||||
|
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
||||||
|
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL;
|
||||||
|
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||||
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, new ProgramInfo(in npdm), executables: programs);
|
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
|
||||||
|
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs);
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
|
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
|
||||||
}
|
}
|
||||||
@@ -573,7 +674,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
MetaLoader metaData = GetDefaultNpdm();
|
MetaLoader metaData = GetDefaultNpdm();
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||||
ProgramInfo programInfo = new ProgramInfo(in npdm);
|
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true);
|
||||||
|
|
||||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
||||||
|
|
||||||
@@ -586,7 +687,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
executable = obj;
|
executable = obj;
|
||||||
|
|
||||||
// homebrew NRO can actually have some data after the actual NRO
|
// Homebrew NRO can actually have some data after the actual NRO.
|
||||||
if (input.Length > obj.FileSize)
|
if (input.Length > obj.FileSize)
|
||||||
{
|
{
|
||||||
input.Position = obj.FileSize;
|
input.Position = obj.FileSize;
|
||||||
@@ -665,7 +766,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
||||||
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
||||||
|
|
||||||
// Explicitly null titleid to disable the shader cache
|
// Explicitly null titleid to disable the shader cache.
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
||||||
_device.Gpu.HostInitalized.Set();
|
_device.Gpu.HostInitalized.Set();
|
||||||
|
|
||||||
|
@@ -21,15 +21,20 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
|
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
|
||||||
|
|
||||||
|
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||||
|
{
|
||||||
|
mode = MemoryManagerMode.SoftwarePageTable;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case MemoryManagerMode.SoftwarePageTable:
|
case MemoryManagerMode.SoftwarePageTable:
|
||||||
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit);
|
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler), for64Bit);
|
||||||
|
|
||||||
case MemoryManagerMode.HostMapped:
|
case MemoryManagerMode.HostMapped:
|
||||||
case MemoryManagerMode.HostMappedUnsafe:
|
case MemoryManagerMode.HostMappedUnsafe:
|
||||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||||
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
|
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
@@ -72,6 +72,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
internal List<NfpDevice> NfpDevices { get; private set; }
|
internal List<NfpDevice> NfpDevices { get; private set; }
|
||||||
|
|
||||||
|
internal SmRegistry SmRegistry { get; private set; }
|
||||||
|
|
||||||
internal ServerBase SmServer { get; private set; }
|
internal ServerBase SmServer { get; private set; }
|
||||||
internal ServerBase BsdServer { get; private set; }
|
internal ServerBase BsdServer { get; private set; }
|
||||||
internal ServerBase AudRenServer { get; private set; }
|
internal ServerBase AudRenServer { get; private set; }
|
||||||
@@ -291,7 +293,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
private void InitializeServices()
|
private void InitializeServices()
|
||||||
{
|
{
|
||||||
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext));
|
SmRegistry = new SmRegistry();
|
||||||
|
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
|
||||||
|
|
||||||
// Wait until SM server thread is done with initialization,
|
// Wait until SM server thread is done with initialization,
|
||||||
// only then doing connections to SM is safe.
|
// only then doing connections to SM is safe.
|
||||||
|
@@ -59,5 +59,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
{
|
{
|
||||||
return GetCurrentThread().Owner;
|
return GetCurrentThread().Owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static KProcess GetProcessByPid(ulong pid)
|
||||||
|
{
|
||||||
|
if (Context.Processes.TryGetValue(pid, out KProcess process))
|
||||||
|
{
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
168
Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs
Normal file
168
Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
{
|
||||||
|
class KCodeMemory : KAutoObject
|
||||||
|
{
|
||||||
|
public KProcess Owner { get; private set; }
|
||||||
|
private readonly KPageList _pageList;
|
||||||
|
private readonly object _lock;
|
||||||
|
private ulong _address;
|
||||||
|
private bool _isOwnerMapped;
|
||||||
|
private bool _isMapped;
|
||||||
|
|
||||||
|
public KCodeMemory(KernelContext context) : base(context)
|
||||||
|
{
|
||||||
|
_pageList = new KPageList();
|
||||||
|
_lock = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult Initialize(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
Owner = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Owner.CpuMemory.Fill(address, size, 0xff);
|
||||||
|
Owner.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_address = address;
|
||||||
|
_isMapped = false;
|
||||||
|
_isOwnerMapped = false;
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult Map(ulong address, ulong size, KMemoryPermission perm)
|
||||||
|
{
|
||||||
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_isMapped)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isMapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_isOwnerMapped)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidState;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
|
KernelResult result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isOwnerMapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult Unmap(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(_isMapped);
|
||||||
|
|
||||||
|
_isMapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapFromOwner(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
KernelResult result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(_isOwnerMapped);
|
||||||
|
|
||||||
|
_isOwnerMapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Destroy()
|
||||||
|
{
|
||||||
|
if (!_isMapped && !_isOwnerMapped)
|
||||||
|
{
|
||||||
|
ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize;
|
||||||
|
|
||||||
|
if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Owner.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,7 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
@@ -12,17 +9,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
private readonly IVirtualMemoryManager _cpuMemory;
|
private readonly IVirtualMemoryManager _cpuMemory;
|
||||||
|
|
||||||
public override bool SupportsMemoryAliasing => true;
|
|
||||||
|
|
||||||
public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context)
|
public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context)
|
||||||
{
|
{
|
||||||
_cpuMemory = cpuMemory;
|
_cpuMemory = cpuMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList)
|
||||||
{
|
{
|
||||||
return _cpuMemory.GetPhysicalRegions(va, size);
|
var ranges = _cpuMemory.GetPhysicalRegions(va, size);
|
||||||
|
foreach (var range in ranges)
|
||||||
|
{
|
||||||
|
pageList.AddRange(range.Address + DramMemoryMap.DramBase, range.Size / PageSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -34,7 +33,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override KernelResult MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission)
|
protected override KernelResult MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission)
|
||||||
{
|
{
|
||||||
var srcRanges = GetPhysicalRegions(src, pagesCount * PageSize);
|
KPageList pageList = new KPageList();
|
||||||
|
GetPhysicalRegions(src, pagesCount * PageSize, pageList);
|
||||||
|
|
||||||
KernelResult result = Reprotect(src, pagesCount, KMemoryPermission.None);
|
KernelResult result = Reprotect(src, pagesCount, KMemoryPermission.None);
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = MapPages(dst, srcRanges, newDstPermission);
|
result = MapPages(dst, pageList, newDstPermission, false, 0);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@@ -59,10 +59,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
ulong size = pagesCount * PageSize;
|
ulong size = pagesCount * PageSize;
|
||||||
|
|
||||||
var srcRanges = GetPhysicalRegions(src, size);
|
KPageList srcPageList = new KPageList();
|
||||||
var dstRanges = GetPhysicalRegions(dst, size);
|
KPageList dstPageList = new KPageList();
|
||||||
|
|
||||||
if (!dstRanges.SequenceEqual(srcRanges))
|
GetPhysicalRegions(src, size, srcPageList);
|
||||||
|
GetPhysicalRegions(dst, size, dstPageList);
|
||||||
|
|
||||||
|
if (!dstPageList.IsEqual(srcPageList))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidMemRange;
|
return KernelResult.InvalidMemRange;
|
||||||
}
|
}
|
||||||
@@ -78,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
KernelResult mapResult = MapPages(dst, dstRanges, oldDstPermission);
|
KernelResult mapResult = MapPages(dst, dstPageList, oldDstPermission, false, 0);
|
||||||
Debug.Assert(mapResult == KernelResult.Success);
|
Debug.Assert(mapResult == KernelResult.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
Context.Memory.Commit(srcPa - DramMemoryMap.DramBase, size);
|
Context.Memory.Commit(srcPa - DramMemoryMap.DramBase, size);
|
||||||
|
|
||||||
_cpuMemory.Map(dstVa, Context.Memory.GetPointer(srcPa - DramMemoryMap.DramBase, size), size);
|
_cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size);
|
||||||
|
|
||||||
if (DramMemoryMap.IsHeapPhysicalAddress(srcPa))
|
if (DramMemoryMap.IsHeapPhysicalAddress(srcPa))
|
||||||
{
|
{
|
||||||
@@ -121,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
Context.Memory.Commit(addr, size);
|
Context.Memory.Commit(addr, size);
|
||||||
|
|
||||||
_cpuMemory.Map(currentVa, Context.Memory.GetPointer(addr, size), size);
|
_cpuMemory.Map(currentVa, addr, size);
|
||||||
|
|
||||||
if (shouldFillPages)
|
if (shouldFillPages)
|
||||||
{
|
{
|
||||||
@@ -136,33 +139,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult MapPages(ulong address, IEnumerable<HostMemoryRange> ranges, KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
ulong currentVa = address;
|
|
||||||
|
|
||||||
foreach (var range in ranges)
|
|
||||||
{
|
|
||||||
ulong size = range.Size;
|
|
||||||
|
|
||||||
ulong pa = GetDramAddressFromHostAddress(range.Address);
|
|
||||||
if (pa != ulong.MaxValue)
|
|
||||||
{
|
|
||||||
pa += DramMemoryMap.DramBase;
|
|
||||||
if (DramMemoryMap.IsHeapPhysicalAddress(pa))
|
|
||||||
{
|
|
||||||
Context.MemoryManager.IncrementPagesReferenceCount(pa, size / PageSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_cpuMemory.Map(currentVa, range.Address, size);
|
|
||||||
|
|
||||||
currentVa += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override KernelResult Unmap(ulong address, ulong pagesCount)
|
protected override KernelResult Unmap(ulong address, ulong pagesCount)
|
||||||
{
|
{
|
||||||
@@ -172,13 +148,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
foreach (var region in regions)
|
foreach (var region in regions)
|
||||||
{
|
{
|
||||||
ulong pa = GetDramAddressFromHostAddress(region.Address);
|
ulong pa = region.Address + DramMemoryMap.DramBase;
|
||||||
if (pa == ulong.MaxValue)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa += DramMemoryMap.DramBase;
|
|
||||||
if (DramMemoryMap.IsHeapPhysicalAddress(pa))
|
if (DramMemoryMap.IsHeapPhysicalAddress(pa))
|
||||||
{
|
{
|
||||||
pagesToClose.AddRange(pa, region.Size / PageSize);
|
pagesToClose.AddRange(pa, region.Size / PageSize);
|
||||||
@@ -217,15 +187,5 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
_cpuMemory.Write(va, data);
|
_cpuMemory.Write(va, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetDramAddressFromHostAddress(nuint hostAddress)
|
|
||||||
{
|
|
||||||
if (hostAddress < (nuint)(ulong)Context.Memory.Pointer || hostAddress >= (nuint)((ulong)Context.Memory.Pointer + Context.Memory.Size))
|
|
||||||
{
|
|
||||||
return ulong.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hostAddress - (ulong)Context.Memory.Pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Memory.Range;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
@@ -73,8 +71,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
private MersenneTwister _randomNumberGenerator;
|
private MersenneTwister _randomNumberGenerator;
|
||||||
|
|
||||||
public abstract bool SupportsMemoryAliasing { get; }
|
|
||||||
|
|
||||||
private MemoryFillValue _heapFillValue;
|
private MemoryFillValue _heapFillValue;
|
||||||
private MemoryFillValue _ipcFillValue;
|
private MemoryFillValue _ipcFillValue;
|
||||||
|
|
||||||
@@ -380,8 +376,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapPages(ulong address, ulong pagesCount, IEnumerable<HostMemoryRange> ranges, MemoryState stateExpected)
|
public KernelResult UnmapPages(ulong address, KPageList pageList, MemoryState stateExpected)
|
||||||
{
|
{
|
||||||
|
ulong pagesCount = pageList.GetPagesCount();
|
||||||
ulong size = pagesCount * PageSize;
|
ulong size = pagesCount * PageSize;
|
||||||
|
|
||||||
ulong endAddr = address + size;
|
ulong endAddr = address + size;
|
||||||
@@ -405,9 +402,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
lock (_blockManager)
|
lock (_blockManager)
|
||||||
{
|
{
|
||||||
var currentRanges = GetPhysicalRegions(address, size);
|
KPageList currentPageList = new KPageList();
|
||||||
|
|
||||||
if (!currentRanges.SequenceEqual(ranges))
|
GetPhysicalRegions(address, size, currentPageList);
|
||||||
|
|
||||||
|
if (!currentPageList.IsEqual(pageList))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidMemRange;
|
return KernelResult.InvalidMemRange;
|
||||||
}
|
}
|
||||||
@@ -1096,6 +1095,77 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapProcessMemory(ulong dst, ulong size, KPageTableBase srcPageTable, ulong src)
|
||||||
|
{
|
||||||
|
lock (_blockManager)
|
||||||
|
{
|
||||||
|
lock (srcPageTable._blockManager)
|
||||||
|
{
|
||||||
|
bool success = CheckRange(
|
||||||
|
dst,
|
||||||
|
size,
|
||||||
|
MemoryState.Mask,
|
||||||
|
MemoryState.ProcessMemory,
|
||||||
|
KMemoryPermission.ReadAndWrite,
|
||||||
|
KMemoryPermission.ReadAndWrite,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
MemoryAttribute.IpcAndDeviceMapped,
|
||||||
|
out _,
|
||||||
|
out _,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
success &= srcPageTable.CheckRange(
|
||||||
|
src,
|
||||||
|
size,
|
||||||
|
MemoryState.MapProcessAllowed,
|
||||||
|
MemoryState.MapProcessAllowed,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
MemoryAttribute.IpcAndDeviceMapped,
|
||||||
|
out _,
|
||||||
|
out _,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPageList srcPageList = new KPageList();
|
||||||
|
KPageList dstPageList = new KPageList();
|
||||||
|
|
||||||
|
srcPageTable.GetPhysicalRegions(src, size, srcPageList);
|
||||||
|
GetPhysicalRegions(dst, size, dstPageList);
|
||||||
|
|
||||||
|
if (!dstPageList.IsEqual(srcPageList))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
|
{
|
||||||
|
return KernelResult.OutOfResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pagesCount = size / PageSize;
|
||||||
|
|
||||||
|
KernelResult result = Unmap(dst, pagesCount);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_blockManager.InsertBlock(dst, pagesCount, MemoryState.Unmapped);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
|
public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
lock (_blockManager)
|
lock (_blockManager)
|
||||||
@@ -1690,11 +1760,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
bool send,
|
bool send,
|
||||||
out ulong dst)
|
out ulong dst)
|
||||||
{
|
{
|
||||||
if (!SupportsMemoryAliasing)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Memory aliasing not supported, can't map IPC buffers.");
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = 0;
|
dst = 0;
|
||||||
|
|
||||||
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
|
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
@@ -1828,7 +1893,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
ulong alignedSize = endAddrTruncated - addressRounded;
|
ulong alignedSize = endAddrTruncated - addressRounded;
|
||||||
|
|
||||||
KernelResult result = MapPages(currentVa, srcPageTable.GetPhysicalRegions(addressRounded, alignedSize), permission);
|
KPageList pageList = new KPageList();
|
||||||
|
srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList);
|
||||||
|
|
||||||
|
KernelResult result = MapPages(currentVa, pageList, permission);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@@ -2026,6 +2094,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
block.RestoreIpcMappingPermission();
|
block.RestoreIpcMappingPermission();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult GetPagesIfStateEquals(
|
||||||
|
ulong address,
|
||||||
|
ulong size,
|
||||||
|
MemoryState stateMask,
|
||||||
|
MemoryState stateExpected,
|
||||||
|
KMemoryPermission permissionMask,
|
||||||
|
KMemoryPermission permissionExpected,
|
||||||
|
MemoryAttribute attributeMask,
|
||||||
|
MemoryAttribute attributeExpected,
|
||||||
|
KPageList pageList)
|
||||||
|
{
|
||||||
|
if (!InsideAddrSpace(address, size))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_blockManager)
|
||||||
|
{
|
||||||
|
if (CheckRange(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
stateMask | MemoryState.IsPoolAllocated,
|
||||||
|
stateExpected | MemoryState.IsPoolAllocated,
|
||||||
|
permissionMask,
|
||||||
|
permissionExpected,
|
||||||
|
attributeMask,
|
||||||
|
attributeExpected,
|
||||||
|
MemoryAttribute.IpcAndDeviceMapped,
|
||||||
|
out _,
|
||||||
|
out _,
|
||||||
|
out _))
|
||||||
|
{
|
||||||
|
GetPhysicalRegions(address, size, pageList);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult BorrowIpcBuffer(ulong address, ulong size)
|
public KernelResult BorrowIpcBuffer(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return SetAttributesAndChangePermission(
|
return SetAttributesAndChangePermission(
|
||||||
@@ -2041,7 +2152,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute.Borrowed);
|
MemoryAttribute.Borrowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult BorrowTransferMemory(List<HostMemoryRange> ranges, ulong address, ulong size, KMemoryPermission permission)
|
public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
return SetAttributesAndChangePermission(
|
return SetAttributesAndChangePermission(
|
||||||
address,
|
address,
|
||||||
@@ -2054,7 +2165,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute.None,
|
MemoryAttribute.None,
|
||||||
permission,
|
permission,
|
||||||
MemoryAttribute.Borrowed,
|
MemoryAttribute.Borrowed,
|
||||||
ranges);
|
pageList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult BorrowCodeMemory(KPageList pageList, ulong address, ulong size)
|
||||||
|
{
|
||||||
|
return SetAttributesAndChangePermission(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
MemoryState.CodeMemoryAllowed,
|
||||||
|
MemoryState.CodeMemoryAllowed,
|
||||||
|
KMemoryPermission.Mask,
|
||||||
|
KMemoryPermission.ReadAndWrite,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
MemoryAttribute.Borrowed,
|
||||||
|
pageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult SetAttributesAndChangePermission(
|
private KernelResult SetAttributesAndChangePermission(
|
||||||
@@ -2068,7 +2195,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute attributeExpected,
|
MemoryAttribute attributeExpected,
|
||||||
KMemoryPermission newPermission,
|
KMemoryPermission newPermission,
|
||||||
MemoryAttribute attributeSetMask,
|
MemoryAttribute attributeSetMask,
|
||||||
List<HostMemoryRange> ranges = null)
|
KPageList pageList = null)
|
||||||
{
|
{
|
||||||
if (address + size <= address || !InsideAddrSpace(address, size))
|
if (address + size <= address || !InsideAddrSpace(address, size))
|
||||||
{
|
{
|
||||||
@@ -2093,7 +2220,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
ulong pagesCount = size / PageSize;
|
ulong pagesCount = size / PageSize;
|
||||||
|
|
||||||
ranges?.AddRange(GetPhysicalRegions(address, size));
|
if (pageList != null)
|
||||||
|
{
|
||||||
|
GetPhysicalRegions(address, pagesCount * PageSize, pageList);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
|
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
{
|
{
|
||||||
@@ -2143,7 +2273,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute.Borrowed);
|
MemoryAttribute.Borrowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnborrowTransferMemory(ulong address, ulong size, List<HostMemoryRange> ranges)
|
public KernelResult UnborrowTransferMemory(ulong address, ulong size, KPageList pageList)
|
||||||
{
|
{
|
||||||
return ClearAttributesAndChangePermission(
|
return ClearAttributesAndChangePermission(
|
||||||
address,
|
address,
|
||||||
@@ -2156,7 +2286,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute.Borrowed,
|
MemoryAttribute.Borrowed,
|
||||||
KMemoryPermission.ReadAndWrite,
|
KMemoryPermission.ReadAndWrite,
|
||||||
MemoryAttribute.Borrowed,
|
MemoryAttribute.Borrowed,
|
||||||
ranges);
|
pageList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnborrowCodeMemory(ulong address, ulong size, KPageList pageList)
|
||||||
|
{
|
||||||
|
return ClearAttributesAndChangePermission(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
MemoryState.CodeMemoryAllowed,
|
||||||
|
MemoryState.CodeMemoryAllowed,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.Borrowed,
|
||||||
|
KMemoryPermission.ReadAndWrite,
|
||||||
|
MemoryAttribute.Borrowed,
|
||||||
|
pageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult ClearAttributesAndChangePermission(
|
private KernelResult ClearAttributesAndChangePermission(
|
||||||
@@ -2170,7 +2316,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute attributeExpected,
|
MemoryAttribute attributeExpected,
|
||||||
KMemoryPermission newPermission,
|
KMemoryPermission newPermission,
|
||||||
MemoryAttribute attributeClearMask,
|
MemoryAttribute attributeClearMask,
|
||||||
List<HostMemoryRange> ranges = null)
|
KPageList pageList = null)
|
||||||
{
|
{
|
||||||
if (address + size <= address || !InsideAddrSpace(address, size))
|
if (address + size <= address || !InsideAddrSpace(address, size))
|
||||||
{
|
{
|
||||||
@@ -2195,11 +2341,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
ulong pagesCount = size / PageSize;
|
ulong pagesCount = size / PageSize;
|
||||||
|
|
||||||
if (ranges != null)
|
if (pageList != null)
|
||||||
{
|
{
|
||||||
var currentRanges = GetPhysicalRegions(address, size);
|
KPageList currentPageList = new KPageList();
|
||||||
|
|
||||||
if (!currentRanges.SequenceEqual(ranges))
|
GetPhysicalRegions(address, pagesCount * PageSize, currentPageList);
|
||||||
|
|
||||||
|
if (!currentPageList.IsEqual(pageList))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidMemRange;
|
return KernelResult.InvalidMemRange;
|
||||||
}
|
}
|
||||||
@@ -2741,8 +2889,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="va">Virtual address of the range</param>
|
/// <param name="va">Virtual address of the range</param>
|
||||||
/// <param name="size">Size of the range</param>
|
/// <param name="size">Size of the range</param>
|
||||||
/// <returns>Array of physical regions</returns>
|
/// <param name="pageList">Page list where the ranges will be added</param>
|
||||||
protected abstract IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size);
|
protected abstract void GetPhysicalRegions(ulong va, ulong size, KPageList pageList);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only span of data from CPU mapped memory.
|
/// Gets a read-only span of data from CPU mapped memory.
|
||||||
@@ -2803,16 +2951,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
/// <returns>Result of the mapping operation</returns>
|
/// <returns>Result of the mapping operation</returns>
|
||||||
protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0);
|
protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps a region of memory into the specified host memory ranges.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Destination virtual address that should be mapped</param>
|
|
||||||
/// <param name="ranges">Ranges of host memory that should be mapped</param>
|
|
||||||
/// <param name="permission">Permission of the region to be mapped</param>
|
|
||||||
/// <returns>Result of the mapping operation</returns>
|
|
||||||
/// <exception cref="NotSupportedException">The implementation does not support memory aliasing</exception>
|
|
||||||
protected abstract KernelResult MapPages(ulong address, IEnumerable<HostMemoryRange> ranges, KMemoryPermission permission);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unmaps a region of memory that was previously mapped with one of the page mapping methods.
|
/// Unmaps a region of memory that was previously mapped with one of the page mapping methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,139 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using Ryujinx.Memory;
|
|
||||||
using Ryujinx.Memory.Range;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|
||||||
{
|
|
||||||
class KPageTableHostMapped : KPageTableBase
|
|
||||||
{
|
|
||||||
private const int CopyChunckSize = 0x100000;
|
|
||||||
|
|
||||||
private readonly IVirtualMemoryManager _cpuMemory;
|
|
||||||
|
|
||||||
public override bool SupportsMemoryAliasing => false;
|
|
||||||
|
|
||||||
public KPageTableHostMapped(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context)
|
|
||||||
{
|
|
||||||
_cpuMemory = cpuMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
|
||||||
{
|
|
||||||
return _cpuMemory.GetPhysicalRegions(va, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override ReadOnlySpan<byte> GetSpan(ulong va, int size)
|
|
||||||
{
|
|
||||||
return _cpuMemory.GetSpan(va, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission)
|
|
||||||
{
|
|
||||||
ulong size = pagesCount * PageSize;
|
|
||||||
|
|
||||||
_cpuMemory.Map(dst, 0, size);
|
|
||||||
|
|
||||||
ulong currentSize = size;
|
|
||||||
while (currentSize > 0)
|
|
||||||
{
|
|
||||||
ulong copySize = Math.Min(currentSize, CopyChunckSize);
|
|
||||||
_cpuMemory.Write(dst, _cpuMemory.GetSpan(src, (int)copySize));
|
|
||||||
currentSize -= copySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult UnmapMemory(ulong dst, ulong src, ulong pagesCount, KMemoryPermission oldDstPermission, KMemoryPermission newSrcPermission)
|
|
||||||
{
|
|
||||||
ulong size = pagesCount * PageSize;
|
|
||||||
|
|
||||||
// TODO: Validation.
|
|
||||||
|
|
||||||
ulong currentSize = size;
|
|
||||||
while (currentSize > 0)
|
|
||||||
{
|
|
||||||
ulong copySize = Math.Min(currentSize, CopyChunckSize);
|
|
||||||
_cpuMemory.Write(src, _cpuMemory.GetSpan(dst, (int)copySize));
|
|
||||||
currentSize -= copySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cpuMemory.Unmap(dst, size);
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages, byte fillValue)
|
|
||||||
{
|
|
||||||
_cpuMemory.Map(dstVa, 0, pagesCount * PageSize);
|
|
||||||
|
|
||||||
if (shouldFillPages)
|
|
||||||
{
|
|
||||||
_cpuMemory.Fill(dstVa, pagesCount * PageSize, fillValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages, byte fillValue)
|
|
||||||
{
|
|
||||||
ulong pagesCount = pageList.GetPagesCount();
|
|
||||||
|
|
||||||
_cpuMemory.Map(address, 0, pagesCount * PageSize);
|
|
||||||
|
|
||||||
if (shouldFillPages)
|
|
||||||
{
|
|
||||||
_cpuMemory.Fill(address, pagesCount * PageSize, fillValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult MapPages(ulong address, IEnumerable<HostMemoryRange> ranges, KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult Unmap(ulong address, ulong pagesCount)
|
|
||||||
{
|
|
||||||
_cpuMemory.Unmap(address, pagesCount * PageSize);
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
// TODO.
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override KernelResult ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
// TODO.
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void SignalMemoryTracking(ulong va, ulong size, bool write)
|
|
||||||
{
|
|
||||||
_cpuMemory.SignalMemoryTracking(va, size, write);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void Write(ulong va, ReadOnlySpan<byte> data)
|
|
||||||
{
|
|
||||||
_cpuMemory.Write(va, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
class KSharedMemory : KAutoObject
|
class KSharedMemory : KAutoObject
|
||||||
{
|
{
|
||||||
private readonly SharedMemoryStorage _storage;
|
private readonly KPageList _pageList;
|
||||||
|
|
||||||
private readonly ulong _ownerPid;
|
private readonly ulong _ownerPid;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
KMemoryPermission ownerPermission,
|
KMemoryPermission ownerPermission,
|
||||||
KMemoryPermission userPermission) : base(context)
|
KMemoryPermission userPermission) : base(context)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_pageList = storage.GetPageList();
|
||||||
_ownerPid = ownerPid;
|
_ownerPid = ownerPid;
|
||||||
_ownerPermission = ownerPermission;
|
_ownerPermission = ownerPermission;
|
||||||
_userPermission = userPermission;
|
_userPermission = userPermission;
|
||||||
@@ -33,10 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
KProcess process,
|
KProcess process,
|
||||||
KMemoryPermission permission)
|
KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize);
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
|
||||||
var pageList = _storage.GetPageList();
|
|
||||||
if (pageList.GetPagesCount() != pagesCountRounded)
|
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
@@ -50,35 +47,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
return KernelResult.InvalidPermission;
|
return KernelResult.InvalidPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelResult result = memoryManager.MapPages(address, pageList, MemoryState.SharedMemory, permission);
|
return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission);
|
||||||
|
|
||||||
if (result == KernelResult.Success && !memoryManager.SupportsMemoryAliasing)
|
|
||||||
{
|
|
||||||
_storage.Borrow(process, address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process)
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapFromProcess(
|
|
||||||
KPageTableBase memoryManager,
|
|
||||||
ulong address,
|
|
||||||
ulong size,
|
|
||||||
KProcess process)
|
|
||||||
{
|
{
|
||||||
ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize);
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
|
|
||||||
var pageList = _storage.GetPageList();
|
|
||||||
ulong pagesCount = pageList.GetPagesCount();
|
|
||||||
|
|
||||||
if (pagesCount != pagesCountRounded)
|
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ranges = _storage.GetRanges();
|
return memoryManager.UnmapPages(address, _pageList, MemoryState.SharedMemory);
|
||||||
|
|
||||||
return memoryManager.UnmapPages(address, pagesCount, ranges, MemoryState.SharedMemory);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Memory.Range;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
@@ -14,9 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
// TODO: Remove when we no longer need to read it from the owner directly.
|
// TODO: Remove when we no longer need to read it from the owner directly.
|
||||||
public KProcess Creator => _creator;
|
public KProcess Creator => _creator;
|
||||||
|
|
||||||
private readonly List<HostMemoryRange> _ranges;
|
private readonly KPageList _pageList;
|
||||||
|
|
||||||
private readonly SharedMemoryStorage _storage;
|
|
||||||
|
|
||||||
public ulong Address { get; private set; }
|
public ulong Address { get; private set; }
|
||||||
public ulong Size { get; private set; }
|
public ulong Size { get; private set; }
|
||||||
@@ -28,12 +24,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
public KTransferMemory(KernelContext context) : base(context)
|
public KTransferMemory(KernelContext context) : base(context)
|
||||||
{
|
{
|
||||||
_ranges = new List<HostMemoryRange>();
|
_pageList = new KPageList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KTransferMemory(KernelContext context, SharedMemoryStorage storage) : base(context)
|
public KTransferMemory(KernelContext context, SharedMemoryStorage storage) : base(context)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_pageList = storage.GetPageList();
|
||||||
Permission = KMemoryPermission.ReadAndWrite;
|
Permission = KMemoryPermission.ReadAndWrite;
|
||||||
|
|
||||||
_hasBeenInitialized = true;
|
_hasBeenInitialized = true;
|
||||||
@@ -46,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
_creator = creator;
|
_creator = creator;
|
||||||
|
|
||||||
KernelResult result = creator.MemoryManager.BorrowTransferMemory(_ranges, address, size, permission);
|
KernelResult result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@@ -71,15 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
KProcess process,
|
KProcess process,
|
||||||
KMemoryPermission permission)
|
KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (_storage == null)
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize);
|
|
||||||
|
|
||||||
var pageList = _storage.GetPageList();
|
|
||||||
if (pageList.GetPagesCount() != pagesCountRounded)
|
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
@@ -91,16 +79,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
||||||
|
|
||||||
KernelResult result = memoryManager.MapPages(address, pageList, state, KMemoryPermission.ReadAndWrite);
|
KernelResult result = memoryManager.MapPages(address, _pageList, state, KMemoryPermission.ReadAndWrite);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
_isMapped = true;
|
_isMapped = true;
|
||||||
|
|
||||||
if (!memoryManager.SupportsMemoryAliasing)
|
|
||||||
{
|
|
||||||
_storage.Borrow(process, address);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -112,26 +95,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
ulong size,
|
ulong size,
|
||||||
KProcess process)
|
KProcess process)
|
||||||
{
|
{
|
||||||
if (_storage == null)
|
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize);
|
|
||||||
|
|
||||||
var pageList = _storage.GetPageList();
|
|
||||||
ulong pagesCount = pageList.GetPagesCount();
|
|
||||||
|
|
||||||
if (pagesCount != pagesCountRounded)
|
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ranges = _storage.GetRanges();
|
|
||||||
|
|
||||||
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
||||||
|
|
||||||
KernelResult result = memoryManager.UnmapPages(address, pagesCount, ranges, state);
|
KernelResult result = memoryManager.UnmapPages(address, _pageList, state);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
@@ -145,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
if (_hasBeenInitialized)
|
if (_hasBeenInitialized)
|
||||||
{
|
{
|
||||||
if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _ranges) != KernelResult.Success)
|
if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,4 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using System;
|
||||||
using Ryujinx.Memory;
|
|
||||||
using Ryujinx.Memory.Range;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
@@ -12,9 +8,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
private readonly KPageList _pageList;
|
private readonly KPageList _pageList;
|
||||||
private readonly ulong _size;
|
private readonly ulong _size;
|
||||||
|
|
||||||
private IVirtualMemoryManager _borrowerMemory;
|
|
||||||
private ulong _borrowerVa;
|
|
||||||
|
|
||||||
public SharedMemoryStorage(KernelContext context, KPageList pageList)
|
public SharedMemoryStorage(KernelContext context, KPageList pageList)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
@@ -29,24 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Borrow(KProcess dstProcess, ulong va)
|
|
||||||
{
|
|
||||||
ulong currentOffset = 0;
|
|
||||||
|
|
||||||
foreach (KPageNode pageNode in _pageList)
|
|
||||||
{
|
|
||||||
ulong address = pageNode.Address - DramMemoryMap.DramBase;
|
|
||||||
ulong size = pageNode.PagesCount * KPageTableBase.PageSize;
|
|
||||||
|
|
||||||
dstProcess.CpuMemory.Write(va + currentOffset, _context.Memory.GetSpan(address + currentOffset, (int)size));
|
|
||||||
|
|
||||||
currentOffset += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
_borrowerMemory = dstProcess.CpuMemory;
|
|
||||||
_borrowerVa = va;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ZeroFill()
|
public void ZeroFill()
|
||||||
{
|
{
|
||||||
for (ulong offset = 0; offset < _size; offset += sizeof(ulong))
|
for (ulong offset = 0; offset < _size; offset += sizeof(ulong))
|
||||||
@@ -56,8 +31,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ref T GetRef<T>(ulong offset) where T : unmanaged
|
public ref T GetRef<T>(ulong offset) where T : unmanaged
|
||||||
{
|
|
||||||
if (_borrowerMemory == null)
|
|
||||||
{
|
{
|
||||||
if (_pageList.Nodes.Count == 1)
|
if (_pageList.Nodes.Count == 1)
|
||||||
{
|
{
|
||||||
@@ -67,33 +40,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
throw new NotImplementedException("Non-contiguous shared memory is not yet supported.");
|
throw new NotImplementedException("Non-contiguous shared memory is not yet supported.");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return ref _borrowerMemory.GetRef<T>(_borrowerVa + offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<HostMemoryRange> GetRanges()
|
|
||||||
{
|
|
||||||
if (_borrowerMemory == null)
|
|
||||||
{
|
|
||||||
var ranges = new List<HostMemoryRange>();
|
|
||||||
|
|
||||||
foreach (KPageNode pageNode in _pageList)
|
|
||||||
{
|
|
||||||
ulong address = pageNode.Address - DramMemoryMap.DramBase;
|
|
||||||
ulong size = pageNode.PagesCount * KPageTableBase.PageSize;
|
|
||||||
|
|
||||||
ranges.Add(new HostMemoryRange(_context.Memory.GetPointer(address, size), size));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _borrowerMemory.GetPhysicalRegions(_borrowerVa, _size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public KPageList GetPageList()
|
public KPageList GetPageList()
|
||||||
{
|
{
|
||||||
|
@@ -60,6 +60,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public KProcessCapabilities Capabilities { get; private set; }
|
public KProcessCapabilities Capabilities { get; private set; }
|
||||||
|
|
||||||
|
public bool AllowCodeMemoryForJit { get; private set; }
|
||||||
|
|
||||||
public ulong TitleId { get; private set; }
|
public ulong TitleId { get; private set; }
|
||||||
public bool IsApplication { get; private set; }
|
public bool IsApplication { get; private set; }
|
||||||
public ulong Pid { get; private set; }
|
public ulong Pid { get; private set; }
|
||||||
@@ -90,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public HleProcessDebugger Debugger { get; private set; }
|
public HleProcessDebugger Debugger { get; private set; }
|
||||||
|
|
||||||
public KProcess(KernelContext context) : base(context)
|
public KProcess(KernelContext context, bool allowCodeMemoryForJit = false) : base(context)
|
||||||
{
|
{
|
||||||
_processLock = new object();
|
_processLock = new object();
|
||||||
_threadingLock = new object();
|
_threadingLock = new object();
|
||||||
@@ -102,6 +104,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
Capabilities = new KProcessCapabilities();
|
Capabilities = new KProcessCapabilities();
|
||||||
|
|
||||||
|
AllowCodeMemoryForJit = allowCodeMemoryForJit;
|
||||||
|
|
||||||
RandomEntropy = new ulong[KScheduler.CpuCoresCount];
|
RandomEntropy = new ulong[KScheduler.CpuCoresCount];
|
||||||
PinnedThreads = new KThread[KScheduler.CpuCoresCount];
|
PinnedThreads = new KThread[KScheduler.CpuCoresCount];
|
||||||
|
|
||||||
@@ -1076,15 +1080,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
|
Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
|
||||||
|
|
||||||
if (Context.AddressSpace is MemoryManagerHostMapped)
|
|
||||||
{
|
|
||||||
MemoryManager = new KPageTableHostMapped(KernelContext, CpuMemory);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MemoryManager = new KPageTable(KernelContext, CpuMemory);
|
MemoryManager = new KPageTable(KernelContext, CpuMemory);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private bool InvalidAccessHandler(ulong va)
|
private bool InvalidAccessHandler(ulong va)
|
||||||
{
|
{
|
||||||
|
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||||
{
|
{
|
||||||
return new ProcessContext(new AddressSpaceManager(addressSpaceSize));
|
return new ProcessContext(new AddressSpaceManager(context.Memory, addressSpaceSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
enum CodeMemoryOperation : uint
|
||||||
|
{
|
||||||
|
Map,
|
||||||
|
MapToOwner,
|
||||||
|
Unmap,
|
||||||
|
UnmapFromOwner
|
||||||
|
};
|
||||||
|
}
|
@@ -7,7 +7,6 @@ using Ryujinx.HLE.HOS.Kernel.Ipc;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -1317,6 +1316,248 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.UnmapPhysicalMemory(address, size);
|
return process.MemoryManager.UnmapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult CreateCodeMemory(ulong address, ulong size, out int handle)
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
if (!PageAligned(address))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(size) || size == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size + address <= address)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KCodeMemory codeMemory = new KCodeMemory(_context);
|
||||||
|
|
||||||
|
using var _ = new OnScopeExit(codeMemory.DecrementReferenceCount);
|
||||||
|
|
||||||
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (!currentProcess.MemoryManager.InsideAddrSpace(address, size))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = codeMemory.Initialize(address, size);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ControlCodeMemory(int handle, CodeMemoryOperation op, ulong address, ulong size, KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
KCodeMemory codeMemory = currentProcess.HandleTable.GetObject<KCodeMemory>(handle);
|
||||||
|
|
||||||
|
// Newer versions of the kernel also returns an error here if the owner and process
|
||||||
|
// where the operation will happen are the same. We do not return an error here
|
||||||
|
// for homebrew because some of them requires this to be patched out to work (for JIT).
|
||||||
|
if (codeMemory == null || (!currentProcess.AllowCodeMemoryForJit && codeMemory.Owner == currentProcess))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case CodeMemoryOperation.Map:
|
||||||
|
if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission != KMemoryPermission.ReadAndWrite)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeMemory.Map(address, size, permission);
|
||||||
|
|
||||||
|
case CodeMemoryOperation.MapToOwner:
|
||||||
|
if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission != KMemoryPermission.Read && permission != KMemoryPermission.ReadAndExecute)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeMemory.MapToOwner(address, size, permission);
|
||||||
|
|
||||||
|
case CodeMemoryOperation.Unmap:
|
||||||
|
if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission != KMemoryPermission.None)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeMemory.Unmap(address, size);
|
||||||
|
|
||||||
|
case CodeMemoryOperation.UnmapFromOwner:
|
||||||
|
if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission != KMemoryPermission.None)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeMemory.UnmapFromOwner(address, size);
|
||||||
|
|
||||||
|
default: return KernelResult.InvalidEnumValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
if (!PageAligned(src))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(size) || size == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission != KMemoryPermission.None &&
|
||||||
|
permission != KMemoryPermission.Read &&
|
||||||
|
permission != KMemoryPermission.ReadAndWrite &&
|
||||||
|
permission != KMemoryPermission.ReadAndExecute)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
|
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
|
||||||
|
|
||||||
|
if (targetProcess == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetProcess.MemoryManager.OutsideAddrSpace(src, size))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult MapProcessMemory(ulong dst, int handle, ulong src, ulong size)
|
||||||
|
{
|
||||||
|
if (!PageAligned(src) || !PageAligned(dst))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(size) || size == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst + size <= dst || src + size <= src)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess dstProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
KProcess srcProcess = dstProcess.HandleTable.GetObject<KProcess>(handle);
|
||||||
|
|
||||||
|
if (srcProcess == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) ||
|
||||||
|
!dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPageList pageList = new KPageList();
|
||||||
|
|
||||||
|
KernelResult result = srcProcess.MemoryManager.GetPagesIfStateEquals(
|
||||||
|
src,
|
||||||
|
size,
|
||||||
|
MemoryState.MapProcessAllowed,
|
||||||
|
MemoryState.MapProcessAllowed,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
KMemoryPermission.None,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
pageList);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapProcessMemory(ulong dst, int handle, ulong src, ulong size)
|
||||||
|
{
|
||||||
|
if (!PageAligned(src) || !PageAligned(dst))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(size) || size == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst + size <= dst || src + size <= src)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess dstProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
KProcess srcProcess = dstProcess.HandleTable.GetObject<KProcess>(handle);
|
||||||
|
|
||||||
|
if (srcProcess == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) ||
|
||||||
|
!dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = dstProcess.MemoryManager.UnmapProcessMemory(dst, size, srcProcess.MemoryManager, src);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(dst) || !PageAligned(src))
|
if (!PageAligned(dst) || !PageAligned(src))
|
||||||
@@ -1391,43 +1632,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
|
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
if (!PageAligned(src))
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permission != KMemoryPermission.None &&
|
|
||||||
permission != KMemoryPermission.Read &&
|
|
||||||
permission != KMemoryPermission.ReadAndWrite &&
|
|
||||||
permission != KMemoryPermission.ReadAndExecute)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidPermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
|
||||||
|
|
||||||
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
|
|
||||||
|
|
||||||
if (targetProcess == null)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetProcess.MemoryManager.OutsideAddrSpace(src, size))
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidMemState;
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool PageAligned(ulong address)
|
private static bool PageAligned(ulong address)
|
||||||
{
|
{
|
||||||
return (address & (KPageTableBase.PageSize - 1)) == 0;
|
return (address & (KPageTableBase.PageSize - 1)) == 0;
|
||||||
|
@@ -143,6 +143,26 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult CreateCodeMemory32([R(1)] uint address, [R(2)] uint size, [R(1)] out int handle)
|
||||||
|
{
|
||||||
|
return _syscall.CreateCodeMemory(address, size, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ControlCodeMemory32(
|
||||||
|
[R(0)] int handle,
|
||||||
|
[R(1)] CodeMemoryOperation op,
|
||||||
|
[R(2)] uint addressLow,
|
||||||
|
[R(3)] uint addressHigh,
|
||||||
|
[R(4)] uint sizeLow,
|
||||||
|
[R(5)] uint sizeHigh,
|
||||||
|
[R(6)] KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
ulong address = addressLow | ((ulong)addressHigh << 32);
|
||||||
|
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
||||||
|
|
||||||
|
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult MapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
|
public KernelResult MapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
return _syscall.MapTransferMemory(handle, address, size, permission);
|
return _syscall.MapTransferMemory(handle, address, size, permission);
|
||||||
@@ -163,6 +183,34 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.UnmapPhysicalMemory(address, size);
|
return _syscall.UnmapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult SetProcessMemoryPermission32(
|
||||||
|
[R(0)] int handle,
|
||||||
|
[R(1)] uint sizeLow,
|
||||||
|
[R(2)] uint srcLow,
|
||||||
|
[R(3)] uint srcHigh,
|
||||||
|
[R(4)] uint sizeHigh,
|
||||||
|
[R(5)] KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
ulong src = srcLow | ((ulong)srcHigh << 32);
|
||||||
|
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
||||||
|
|
||||||
|
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult MapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
|
||||||
|
{
|
||||||
|
ulong src = srcLow | ((ulong)srcHigh << 32);
|
||||||
|
|
||||||
|
return _syscall.MapProcessMemory(dst, handle, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
|
||||||
|
{
|
||||||
|
ulong src = srcLow | ((ulong)srcHigh << 32);
|
||||||
|
|
||||||
|
return _syscall.UnmapProcessMemory(dst, handle, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult MapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
|
public KernelResult MapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
|
||||||
{
|
{
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
ulong src = srcLow | ((ulong)srcHigh << 32);
|
||||||
@@ -181,20 +229,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission32(
|
|
||||||
[R(0)] int handle,
|
|
||||||
[R(1)] uint sizeLow,
|
|
||||||
[R(2)] uint srcLow,
|
|
||||||
[R(3)] uint srcHigh,
|
|
||||||
[R(4)] uint sizeHigh,
|
|
||||||
[R(5)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System
|
// System
|
||||||
|
|
||||||
public void ExitProcess32()
|
public void ExitProcess32()
|
||||||
|
@@ -160,6 +160,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult CreateCodeMemory64([R(1)] ulong address, [R(2)] ulong size, [R(1)] out int handle)
|
||||||
|
{
|
||||||
|
return _syscall.CreateCodeMemory(address, size, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ControlCodeMemory64([R(0)] int handle, [R(1)] CodeMemoryOperation op, [R(2)] ulong address, [R(3)] ulong size, [R(4)] KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult MapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
public KernelResult MapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
return _syscall.MapTransferMemory(handle, address, size, permission);
|
return _syscall.MapTransferMemory(handle, address, size, permission);
|
||||||
@@ -180,6 +190,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.UnmapPhysicalMemory(address, size);
|
return _syscall.UnmapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
||||||
|
{
|
||||||
|
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult MapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
|
||||||
|
{
|
||||||
|
return _syscall.MapProcessMemory(dst, handle, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
|
||||||
|
{
|
||||||
|
return _syscall.UnmapProcessMemory(dst, handle, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult MapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
|
public KernelResult MapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
|
||||||
{
|
{
|
||||||
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
|
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
|
||||||
@@ -190,11 +215,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System
|
// System
|
||||||
|
|
||||||
public void ExitProcess64()
|
public void ExitProcess64()
|
||||||
|
@@ -78,6 +78,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
|
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
|
||||||
{ 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
|
{ 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
|
||||||
{ 0x45, nameof(Syscall64.CreateEvent64) },
|
{ 0x45, nameof(Syscall64.CreateEvent64) },
|
||||||
|
{ 0x4b, nameof(Syscall64.CreateCodeMemory64) },
|
||||||
|
{ 0x4c, nameof(Syscall64.ControlCodeMemory64) },
|
||||||
{ 0x51, nameof(Syscall64.MapTransferMemory64) },
|
{ 0x51, nameof(Syscall64.MapTransferMemory64) },
|
||||||
{ 0x52, nameof(Syscall64.UnmapTransferMemory64) },
|
{ 0x52, nameof(Syscall64.UnmapTransferMemory64) },
|
||||||
{ 0x65, nameof(Syscall64.GetProcessList64) },
|
{ 0x65, nameof(Syscall64.GetProcessList64) },
|
||||||
@@ -86,6 +88,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x71, nameof(Syscall64.ManageNamedPort64) },
|
{ 0x71, nameof(Syscall64.ManageNamedPort64) },
|
||||||
{ 0x72, nameof(Syscall64.ConnectToPort64) },
|
{ 0x72, nameof(Syscall64.ConnectToPort64) },
|
||||||
{ 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
|
{ 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
|
||||||
|
{ 0x74, nameof(Syscall64.MapProcessMemory64) },
|
||||||
|
{ 0x75, nameof(Syscall64.UnmapProcessMemory64) },
|
||||||
{ 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
|
{ 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
|
||||||
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
|
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
|
||||||
{ 0x7B, nameof(Syscall64.TerminateProcess64) },
|
{ 0x7B, nameof(Syscall64.TerminateProcess64) },
|
||||||
@@ -152,6 +156,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x41, nameof(Syscall32.AcceptSession32) },
|
{ 0x41, nameof(Syscall32.AcceptSession32) },
|
||||||
{ 0x43, nameof(Syscall32.ReplyAndReceive32) },
|
{ 0x43, nameof(Syscall32.ReplyAndReceive32) },
|
||||||
{ 0x45, nameof(Syscall32.CreateEvent32) },
|
{ 0x45, nameof(Syscall32.CreateEvent32) },
|
||||||
|
{ 0x4b, nameof(Syscall32.CreateCodeMemory32) },
|
||||||
|
{ 0x4c, nameof(Syscall32.ControlCodeMemory32) },
|
||||||
{ 0x51, nameof(Syscall32.MapTransferMemory32) },
|
{ 0x51, nameof(Syscall32.MapTransferMemory32) },
|
||||||
{ 0x52, nameof(Syscall32.UnmapTransferMemory32) },
|
{ 0x52, nameof(Syscall32.UnmapTransferMemory32) },
|
||||||
{ 0x5F, nameof(Syscall32.FlushProcessDataCache32) },
|
{ 0x5F, nameof(Syscall32.FlushProcessDataCache32) },
|
||||||
@@ -161,6 +167,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x71, nameof(Syscall32.ManageNamedPort32) },
|
{ 0x71, nameof(Syscall32.ManageNamedPort32) },
|
||||||
{ 0x72, nameof(Syscall32.ConnectToPort32) },
|
{ 0x72, nameof(Syscall32.ConnectToPort32) },
|
||||||
{ 0x73, nameof(Syscall32.SetProcessMemoryPermission32) },
|
{ 0x73, nameof(Syscall32.SetProcessMemoryPermission32) },
|
||||||
|
{ 0x74, nameof(Syscall32.MapProcessMemory32) },
|
||||||
|
{ 0x75, nameof(Syscall32.UnmapProcessMemory32) },
|
||||||
{ 0x77, nameof(Syscall32.MapProcessCodeMemory32) },
|
{ 0x77, nameof(Syscall32.MapProcessCodeMemory32) },
|
||||||
{ 0x78, nameof(Syscall32.UnmapProcessCodeMemory32) },
|
{ 0x78, nameof(Syscall32.UnmapProcessCodeMemory32) },
|
||||||
{ 0x7B, nameof(Syscall32.TerminateProcess32) },
|
{ 0x7B, nameof(Syscall32.TerminateProcess32) },
|
||||||
|
@@ -20,11 +20,13 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
public string Name;
|
public string Name;
|
||||||
public ulong ProgramId;
|
public ulong ProgramId;
|
||||||
|
public bool AllowCodeMemoryForJit;
|
||||||
|
|
||||||
public ProgramInfo(in Npdm npdm)
|
public ProgramInfo(in Npdm npdm, bool allowCodeMemoryForJit)
|
||||||
{
|
{
|
||||||
Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName);
|
Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName);
|
||||||
ProgramId = npdm.Aci.Value.ProgramId.Value;
|
ProgramId = npdm.Aci.Value.ProgramId.Value;
|
||||||
|
AllowCodeMemoryForJit = allowCodeMemoryForJit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +143,13 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool LoadNsos(KernelContext context, out ProcessTamperInfo tamperInfo, MetaLoader metaData, ProgramInfo programInfo, byte[] arguments = null, params IExecutable[] executables)
|
public static bool LoadNsos(
|
||||||
|
KernelContext context,
|
||||||
|
out ProcessTamperInfo tamperInfo,
|
||||||
|
MetaLoader metaData,
|
||||||
|
ProgramInfo programInfo,
|
||||||
|
byte[] arguments = null,
|
||||||
|
params IExecutable[] executables)
|
||||||
{
|
{
|
||||||
LibHac.Result rc = metaData.GetNpdm(out var npdm);
|
LibHac.Result rc = metaData.GetNpdm(out var npdm);
|
||||||
|
|
||||||
@@ -243,7 +251,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess process = new KProcess(context);
|
KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit);
|
||||||
|
|
||||||
MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Value.Flags >> 2) & 0xf);
|
MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Value.Flags >> 2) & 0xf);
|
||||||
|
|
||||||
|
@@ -2,9 +2,12 @@ using LibHac;
|
|||||||
using LibHac.Account;
|
using LibHac.Account;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
@@ -15,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
|
|||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||||
using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
|
using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
|
||||||
@@ -37,6 +41,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
private int _notificationStorageChannelEventHandle;
|
private int _notificationStorageChannelEventHandle;
|
||||||
private int _healthWarningDisappearedSystemEventHandle;
|
private int _healthWarningDisappearedSystemEventHandle;
|
||||||
|
|
||||||
|
private bool _gamePlayRecordingState;
|
||||||
|
|
||||||
|
private int _jitLoaded;
|
||||||
|
|
||||||
private HorizonClient _horizon;
|
private HorizonClient _horizon;
|
||||||
|
|
||||||
public IApplicationFunctions(Horizon system)
|
public IApplicationFunctions(Horizon system)
|
||||||
@@ -342,6 +350,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(65)] // 3.0.0+
|
||||||
|
// IsGamePlayRecordingSupported() -> u8
|
||||||
|
public ResultCode IsGamePlayRecordingSupported(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write(_gamePlayRecordingState);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(66)] // 3.0.0+
|
[CommandHipc(66)] // 3.0.0+
|
||||||
// InitializeGamePlayRecording(u64, handle<copy>)
|
// InitializeGamePlayRecording(u64, handle<copy>)
|
||||||
public ResultCode InitializeGamePlayRecording(ServiceCtx context)
|
public ResultCode InitializeGamePlayRecording(ServiceCtx context)
|
||||||
@@ -355,9 +372,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
// SetGamePlayRecordingState(u32)
|
// SetGamePlayRecordingState(u32)
|
||||||
public ResultCode SetGamePlayRecordingState(ServiceCtx context)
|
public ResultCode SetGamePlayRecordingState(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int state = context.RequestData.ReadInt32();
|
_gamePlayRecordingState = context.RequestData.ReadInt32() != 0;
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { state });
|
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { _gamePlayRecordingState });
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@@ -631,5 +648,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(1001)] // 10.0.0+
|
||||||
|
// PrepareForJit()
|
||||||
|
public ResultCode PrepareForJit(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (Interlocked.Exchange(ref _jitLoaded, 1) == 0)
|
||||||
|
{
|
||||||
|
string jitPath = context.Device.System.ContentManager.GetInstalledContentPath(0x010000000000003B, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
string filePath = context.Device.FileSystem.SwitchPathToSystemPath(jitPath);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
{
|
||||||
|
throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Device.Application.LoadServiceNca(filePath);
|
||||||
|
|
||||||
|
// FIXME: Most likely not how this should be done?
|
||||||
|
while (!context.Device.System.SmRegistry.IsServiceRegistered("jit:u"))
|
||||||
|
{
|
||||||
|
context.Device.System.SmRegistry.WaitForServiceRegistration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
using Concentus.Structs;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
||||||
|
{
|
||||||
|
class Decoder : IDecoder
|
||||||
|
{
|
||||||
|
private readonly OpusDecoder _decoder;
|
||||||
|
|
||||||
|
public int SampleRate => _decoder.SampleRate;
|
||||||
|
public int ChannelsCount => _decoder.NumChannels;
|
||||||
|
|
||||||
|
public Decoder(int sampleRate, int channelsCount)
|
||||||
|
{
|
||||||
|
_decoder = new OpusDecoder(sampleRate, channelsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
|
||||||
|
{
|
||||||
|
return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetState()
|
||||||
|
{
|
||||||
|
_decoder.ResetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,92 @@
|
|||||||
|
using Concentus;
|
||||||
|
using Concentus.Enums;
|
||||||
|
using Concentus.Structs;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Audio.Types;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
||||||
|
{
|
||||||
|
static class DecoderCommon
|
||||||
|
{
|
||||||
|
private static ResultCode GetPacketNumSamples(this IDecoder decoder, out int numSamples, byte[] packet)
|
||||||
|
{
|
||||||
|
int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate);
|
||||||
|
|
||||||
|
numSamples = result;
|
||||||
|
|
||||||
|
if (result == OpusError.OPUS_INVALID_PACKET)
|
||||||
|
{
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
else if (result == OpusError.OPUS_BAD_ARG)
|
||||||
|
{
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResultCode DecodeInterleaved(
|
||||||
|
this IDecoder decoder,
|
||||||
|
bool reset,
|
||||||
|
ReadOnlySpan<byte> input,
|
||||||
|
out short[] outPcmData,
|
||||||
|
ulong outputSize,
|
||||||
|
out uint outConsumed,
|
||||||
|
out int outSamples)
|
||||||
|
{
|
||||||
|
outPcmData = null;
|
||||||
|
outConsumed = 0;
|
||||||
|
outSamples = 0;
|
||||||
|
|
||||||
|
int streamSize = input.Length;
|
||||||
|
|
||||||
|
if (streamSize < Unsafe.SizeOf<OpusPacketHeader>())
|
||||||
|
{
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpusPacketHeader header = OpusPacketHeader.FromSpan(input);
|
||||||
|
int headerSize = Unsafe.SizeOf<OpusPacketHeader>();
|
||||||
|
uint totalSize = header.length + (uint)headerSize;
|
||||||
|
|
||||||
|
if (totalSize > streamSize)
|
||||||
|
{
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] opusData = input.Slice(headerSize, (int)header.length).ToArray();
|
||||||
|
|
||||||
|
ResultCode result = decoder.GetPacketNumSamples(out int numSamples, opusData);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
if ((uint)numSamples * (uint)decoder.ChannelsCount * sizeof(short) > outputSize)
|
||||||
|
{
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPcmData = new short[numSamples * decoder.ChannelsCount];
|
||||||
|
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
decoder.ResetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount);
|
||||||
|
outConsumed = totalSize;
|
||||||
|
}
|
||||||
|
catch (OpusException)
|
||||||
|
{
|
||||||
|
// TODO: as OpusException doesn't provide us the exact error code, this is kind of inaccurate in some cases...
|
||||||
|
return ResultCode.OpusInvalidInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
||||||
|
{
|
||||||
|
interface IDecoder
|
||||||
|
{
|
||||||
|
int SampleRate { get; }
|
||||||
|
int ChannelsCount { get; }
|
||||||
|
|
||||||
|
int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize);
|
||||||
|
void ResetState();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,244 +1,112 @@
|
|||||||
using Concentus;
|
|
||||||
using Concentus.Enums;
|
|
||||||
using Concentus.Structs;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Audio.Types;
|
using Ryujinx.HLE.HOS.Services.Audio.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
||||||
{
|
{
|
||||||
class IHardwareOpusDecoder : IpcService
|
class IHardwareOpusDecoder : IpcService
|
||||||
{
|
{
|
||||||
private int _sampleRate;
|
private readonly IDecoder _decoder;
|
||||||
private int _channelsCount;
|
private readonly OpusDecoderFlags _flags;
|
||||||
private bool _reset;
|
|
||||||
|
|
||||||
private OpusDecoder _decoder;
|
public IHardwareOpusDecoder(int sampleRate, int channelsCount, OpusDecoderFlags flags)
|
||||||
|
|
||||||
public IHardwareOpusDecoder(int sampleRate, int channelsCount)
|
|
||||||
{
|
{
|
||||||
_sampleRate = sampleRate;
|
_decoder = new Decoder(sampleRate, channelsCount);
|
||||||
_channelsCount = channelsCount;
|
_flags = flags;
|
||||||
_reset = false;
|
|
||||||
|
|
||||||
_decoder = new OpusDecoder(sampleRate, channelsCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode GetPacketNumSamples(out int numSamples, byte[] packet)
|
public IHardwareOpusDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, OpusDecoderFlags flags, byte[] mapping)
|
||||||
{
|
{
|
||||||
int result = OpusPacketInfo.GetNumSamples(_decoder, packet, 0, packet.Length);
|
_decoder = new MultiSampleDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
|
||||||
|
_flags = flags;
|
||||||
numSamples = result;
|
|
||||||
|
|
||||||
if (result == OpusError.OPUS_INVALID_PACKET)
|
|
||||||
{
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
else if (result == OpusError.OPUS_BAD_ARG)
|
|
||||||
{
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResultCode DecodeInterleavedInternal(BinaryReader input, out short[] outPcmData, long outputSize, out uint outConsumed, out int outSamples)
|
|
||||||
{
|
|
||||||
outPcmData = null;
|
|
||||||
outConsumed = 0;
|
|
||||||
outSamples = 0;
|
|
||||||
|
|
||||||
long streamSize = input.BaseStream.Length;
|
|
||||||
|
|
||||||
if (streamSize < Marshal.SizeOf<OpusPacketHeader>())
|
|
||||||
{
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpusPacketHeader header = OpusPacketHeader.FromStream(input);
|
|
||||||
|
|
||||||
uint totalSize = header.length + (uint)Marshal.SizeOf<OpusPacketHeader>();
|
|
||||||
|
|
||||||
if (totalSize > streamSize)
|
|
||||||
{
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] opusData = input.ReadBytes((int)header.length);
|
|
||||||
|
|
||||||
ResultCode result = GetPacketNumSamples(out int numSamples, opusData);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
|
||||||
if ((uint)numSamples * (uint)_channelsCount * sizeof(short) > outputSize)
|
|
||||||
{
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
outPcmData = new short[numSamples * _channelsCount];
|
|
||||||
|
|
||||||
if (_reset)
|
|
||||||
{
|
|
||||||
_reset = false;
|
|
||||||
|
|
||||||
_decoder.ResetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
outSamples = _decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / _channelsCount);
|
|
||||||
outConsumed = totalSize;
|
|
||||||
}
|
|
||||||
catch (OpusException)
|
|
||||||
{
|
|
||||||
// TODO: as OpusException doesn't provide us the exact error code, this is kind of inaccurate in some cases...
|
|
||||||
return ResultCode.OpusInvalidInput;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(0)]
|
[CommandHipc(0)]
|
||||||
// DecodeInterleaved(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>)
|
// DecodeInterleavedOld(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>)
|
||||||
public ResultCode DecodeInterleavedOriginal(ServiceCtx context)
|
public ResultCode DecodeInterleavedOld(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false);
|
||||||
|
|
||||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
|
||||||
ulong inSize = context.Request.SendBuff[0].Size;
|
|
||||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
byte[] buffer = new byte[inSize];
|
|
||||||
|
|
||||||
context.Memory.Read(inPosition, buffer);
|
|
||||||
|
|
||||||
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
|
|
||||||
{
|
|
||||||
result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
|
||||||
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
|
|
||||||
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
|
|
||||||
context.Memory.Write(outputPosition, pcmDataBytes);
|
|
||||||
|
|
||||||
context.ResponseData.Write(outConsumed);
|
|
||||||
context.ResponseData.Write(outSamples);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
[CommandHipc(2)]
|
||||||
|
// DecodeInterleavedForMultiStreamOld(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>)
|
||||||
|
public ResultCode DecodeInterleavedForMultiStreamOld(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(4)] // 6.0.0+
|
[CommandHipc(4)] // 6.0.0+
|
||||||
// DecodeInterleavedWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
// DecodeInterleavedWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
public ResultCode DecodeInterleavedWithPerfOld(ServiceCtx context)
|
public ResultCode DecodeInterleavedWithPerfOld(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true);
|
||||||
|
|
||||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
|
||||||
ulong inSize = context.Request.SendBuff[0].Size;
|
|
||||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
byte[] buffer = new byte[inSize];
|
|
||||||
|
|
||||||
context.Memory.Read(inPosition, buffer);
|
|
||||||
|
|
||||||
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
|
|
||||||
{
|
|
||||||
result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
|
||||||
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
|
|
||||||
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
|
|
||||||
context.Memory.Write(outputPosition, pcmDataBytes);
|
|
||||||
|
|
||||||
context.ResponseData.Write(outConsumed);
|
|
||||||
context.ResponseData.Write(outSamples);
|
|
||||||
|
|
||||||
// This is the time the DSP took to process the request, TODO: fill this.
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
[CommandHipc(5)] // 6.0.0+
|
||||||
|
// DecodeInterleavedForMultiStreamWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
|
public ResultCode DecodeInterleavedForMultiStreamWithPerfOld(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(6)] // 6.0.0+
|
[CommandHipc(6)] // 6.0.0+
|
||||||
// DecodeInterleavedOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
// DecodeInterleavedWithPerfAndResetOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
public ResultCode DecodeInterleavedOld(ServiceCtx context)
|
public ResultCode DecodeInterleavedWithPerfAndResetOld(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
bool reset = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
_reset = context.RequestData.ReadBoolean();
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true);
|
||||||
|
|
||||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
|
||||||
ulong inSize = context.Request.SendBuff[0].Size;
|
|
||||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
byte[] buffer = new byte[inSize];
|
|
||||||
|
|
||||||
context.Memory.Read(inPosition, buffer);
|
|
||||||
|
|
||||||
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
|
|
||||||
{
|
|
||||||
result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
|
||||||
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
|
|
||||||
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
|
|
||||||
context.Memory.Write(outputPosition, pcmDataBytes);
|
|
||||||
|
|
||||||
context.ResponseData.Write(outConsumed);
|
|
||||||
context.ResponseData.Write(outSamples);
|
|
||||||
|
|
||||||
// This is the time the DSP took to process the request, TODO: fill this.
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
[CommandHipc(7)] // 6.0.0+
|
||||||
|
// DecodeInterleavedForMultiStreamWithPerfAndResetOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
|
public ResultCode DecodeInterleavedForMultiStreamWithPerfAndResetOld(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool reset = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
|
return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(8)] // 7.0.0+
|
[CommandHipc(8)] // 7.0.0+
|
||||||
// DecodeInterleaved(bool reset, buffer<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
// DecodeInterleaved(bool reset, buffer<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
public ResultCode DecodeInterleaved(ServiceCtx context)
|
public ResultCode DecodeInterleaved(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
bool reset = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
_reset = context.RequestData.ReadBoolean();
|
return DecodeInterleavedInternal(context, _flags, reset, withPerf: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(9)] // 7.0.0+
|
||||||
|
// DecodeInterleavedForMultiStream(bool reset, buffer<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>)
|
||||||
|
public ResultCode DecodeInterleavedForMultiStream(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool reset = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
|
return DecodeInterleavedInternal(context, _flags, reset, withPerf: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultCode DecodeInterleavedInternal(ServiceCtx context, OpusDecoderFlags flags, bool reset, bool withPerf)
|
||||||
|
{
|
||||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
ulong inPosition = context.Request.SendBuff[0].Position;
|
||||||
ulong inSize = context.Request.SendBuff[0].Size;
|
ulong inSize = context.Request.SendBuff[0].Size;
|
||||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
||||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
||||||
|
|
||||||
byte[] buffer = new byte[inSize];
|
ReadOnlySpan<byte> input = context.Memory.GetSpan(inPosition, (int)inSize);
|
||||||
|
|
||||||
context.Memory.Read(inPosition, buffer);
|
ResultCode result = _decoder.DecodeInterleaved(reset, input, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples);
|
||||||
|
|
||||||
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
|
|
||||||
{
|
|
||||||
result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
|
context.Memory.Write(outputPosition, MemoryMarshal.Cast<short, byte>(outPcmData.AsSpan()));
|
||||||
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
|
|
||||||
context.Memory.Write(outputPosition, pcmDataBytes);
|
|
||||||
|
|
||||||
context.ResponseData.Write(outConsumed);
|
context.ResponseData.Write(outConsumed);
|
||||||
context.ResponseData.Write(outSamples);
|
context.ResponseData.Write(outSamples);
|
||||||
|
|
||||||
|
if (withPerf)
|
||||||
|
{
|
||||||
// This is the time the DSP took to process the request, TODO: fill this.
|
// This is the time the DSP took to process the request, TODO: fill this.
|
||||||
context.ResponseData.Write(0);
|
context.ResponseData.Write(0UL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
using Concentus.Structs;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
|
||||||
|
{
|
||||||
|
class MultiSampleDecoder : IDecoder
|
||||||
|
{
|
||||||
|
private readonly OpusMSDecoder _decoder;
|
||||||
|
|
||||||
|
public int SampleRate => _decoder.SampleRate;
|
||||||
|
public int ChannelsCount { get; }
|
||||||
|
|
||||||
|
public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping)
|
||||||
|
{
|
||||||
|
ChannelsCount = channelsCount;
|
||||||
|
_decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
|
||||||
|
{
|
||||||
|
return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetState()
|
||||||
|
{
|
||||||
|
_decoder.ResetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager;
|
using Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager;
|
||||||
using Ryujinx.HLE.HOS.Services.Audio.Types;
|
using Ryujinx.HLE.HOS.Services.Audio.Types;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Audio
|
namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
{
|
{
|
||||||
@@ -16,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
int sampleRate = context.RequestData.ReadInt32();
|
int sampleRate = context.RequestData.ReadInt32();
|
||||||
int channelsCount = context.RequestData.ReadInt32();
|
int channelsCount = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount));
|
MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount, OpusDecoderFlags.None));
|
||||||
|
|
||||||
// Close transfer memory immediately as we don't use it.
|
// Close transfer memory immediately as we don't use it.
|
||||||
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
@@ -28,11 +29,50 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
// GetWorkBufferSize(bytes<8, 4>) -> u32
|
// GetWorkBufferSize(bytes<8, 4>) -> u32
|
||||||
public ResultCode GetWorkBufferSize(ServiceCtx context)
|
public ResultCode GetWorkBufferSize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// NOTE: The sample rate is ignored because it is fixed to 48KHz.
|
|
||||||
int sampleRate = context.RequestData.ReadInt32();
|
int sampleRate = context.RequestData.ReadInt32();
|
||||||
int channelsCount = context.RequestData.ReadInt32();
|
int channelsCount = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
context.ResponseData.Write(GetOpusDecoderSize(channelsCount));
|
int opusDecoderSize = GetOpusDecoderSize(channelsCount);
|
||||||
|
|
||||||
|
int frameSize = BitUtils.AlignUp(channelsCount * 1920 / (48000 / sampleRate), 64);
|
||||||
|
int totalSize = opusDecoderSize + 1536 + frameSize;
|
||||||
|
|
||||||
|
context.ResponseData.Write(totalSize);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(2)] // 3.0.0+
|
||||||
|
// InitializeForMultiStream(u32, handle<copy>, buffer<unknown<0x110>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder>
|
||||||
|
public ResultCode InitializeForMultiStream(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong parametersAddress = context.Request.PtrBuff[0].Position;
|
||||||
|
|
||||||
|
OpusMultiStreamParameters parameters = context.Memory.Read<OpusMultiStreamParameters>(parametersAddress);
|
||||||
|
|
||||||
|
MakeObject(context, new IHardwareOpusDecoder(parameters.SampleRate, parameters.ChannelsCount, OpusDecoderFlags.None));
|
||||||
|
|
||||||
|
// Close transfer memory immediately as we don't use it.
|
||||||
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(3)] // 3.0.0+
|
||||||
|
// GetWorkBufferSizeForMultiStream(buffer<unknown<0x110>, 0x19>) -> u32
|
||||||
|
public ResultCode GetWorkBufferSizeForMultiStream(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong parametersAddress = context.Request.PtrBuff[0].Position;
|
||||||
|
|
||||||
|
OpusMultiStreamParameters parameters = context.Memory.Read<OpusMultiStreamParameters>(parametersAddress);
|
||||||
|
|
||||||
|
int opusDecoderSize = GetOpusMultistreamDecoderSize(parameters.NumberOfStreams, parameters.NumberOfStereoStreams);
|
||||||
|
|
||||||
|
int streamSize = BitUtils.AlignUp(parameters.NumberOfStreams * 1500, 64);
|
||||||
|
int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * 1920 / (48000 / parameters.SampleRate), 64);
|
||||||
|
int totalSize = opusDecoderSize + streamSize + frameSize;
|
||||||
|
|
||||||
|
context.ResponseData.Write(totalSize);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@@ -44,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
OpusParametersEx parameters = context.RequestData.ReadStruct<OpusParametersEx>();
|
OpusParametersEx parameters = context.RequestData.ReadStruct<OpusParametersEx>();
|
||||||
|
|
||||||
// UseLargeFrameSize can be ignored due to not relying on fixed size buffers for storing the decoded result.
|
// UseLargeFrameSize can be ignored due to not relying on fixed size buffers for storing the decoded result.
|
||||||
MakeObject(context, new IHardwareOpusDecoder(parameters.SampleRate, parameters.ChannelCount));
|
MakeObject(context, new IHardwareOpusDecoder(parameters.SampleRate, parameters.ChannelsCount, parameters.Flags));
|
||||||
|
|
||||||
// Close transfer memory immediately as we don't use it.
|
// Close transfer memory immediately as we don't use it.
|
||||||
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
@@ -58,15 +98,84 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
{
|
{
|
||||||
OpusParametersEx parameters = context.RequestData.ReadStruct<OpusParametersEx>();
|
OpusParametersEx parameters = context.RequestData.ReadStruct<OpusParametersEx>();
|
||||||
|
|
||||||
// NOTE: The sample rate is ignored because it is fixed to 48KHz.
|
int opusDecoderSize = GetOpusDecoderSize(parameters.ChannelsCount);
|
||||||
context.ResponseData.Write(GetOpusDecoderSize(parameters.ChannelCount));
|
|
||||||
|
int frameSizeMono48KHz = parameters.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920;
|
||||||
|
int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * frameSizeMono48KHz / (48000 / parameters.SampleRate), 64);
|
||||||
|
int totalSize = opusDecoderSize + 1536 + frameSize;
|
||||||
|
|
||||||
|
context.ResponseData.Write(totalSize);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(6)] // 12.0.0+
|
||||||
|
// InitializeForMultiStreamEx(u32, handle<copy>, buffer<unknown<0x118>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder>
|
||||||
|
public ResultCode InitializeForMultiStreamEx(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong parametersAddress = context.Request.PtrBuff[0].Position;
|
||||||
|
|
||||||
|
OpusMultiStreamParametersEx parameters = context.Memory.Read<OpusMultiStreamParametersEx>(parametersAddress);
|
||||||
|
|
||||||
|
byte[] mappings = MemoryMarshal.Cast<uint, byte>(parameters.ChannelMappings.ToSpan()).ToArray();
|
||||||
|
|
||||||
|
// UseLargeFrameSize can be ignored due to not relying on fixed size buffers for storing the decoded result.
|
||||||
|
MakeObject(context, new IHardwareOpusDecoder(
|
||||||
|
parameters.SampleRate,
|
||||||
|
parameters.ChannelsCount,
|
||||||
|
parameters.NumberOfStreams,
|
||||||
|
parameters.NumberOfStereoStreams,
|
||||||
|
parameters.Flags,
|
||||||
|
mappings));
|
||||||
|
|
||||||
|
// Close transfer memory immediately as we don't use it.
|
||||||
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(7)] // 12.0.0+
|
||||||
|
// GetWorkBufferSizeForMultiStreamEx(buffer<unknown<0x118>, 0x19>) -> u32
|
||||||
|
public ResultCode GetWorkBufferSizeForMultiStreamEx(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong parametersAddress = context.Request.PtrBuff[0].Position;
|
||||||
|
|
||||||
|
OpusMultiStreamParametersEx parameters = context.Memory.Read<OpusMultiStreamParametersEx>(parametersAddress);
|
||||||
|
|
||||||
|
int opusDecoderSize = GetOpusMultistreamDecoderSize(parameters.NumberOfStreams, parameters.NumberOfStereoStreams);
|
||||||
|
|
||||||
|
int frameSizeMono48KHz = parameters.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920;
|
||||||
|
int streamSize = BitUtils.AlignUp(parameters.NumberOfStreams * 1500, 64);
|
||||||
|
int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * frameSizeMono48KHz / (48000 / parameters.SampleRate), 64);
|
||||||
|
int totalSize = opusDecoderSize + streamSize + frameSize;
|
||||||
|
|
||||||
|
context.ResponseData.Write(totalSize);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetOpusMultistreamDecoderSize(int streams, int coupledStreams)
|
||||||
|
{
|
||||||
|
if (streams < 1 || coupledStreams > streams || coupledStreams < 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coupledSize = GetOpusDecoderSize(2);
|
||||||
|
int monoSize = GetOpusDecoderSize(1);
|
||||||
|
|
||||||
|
return Align4(monoSize - GetOpusDecoderAllocSize(1)) * (streams - coupledStreams) +
|
||||||
|
Align4(coupledSize - GetOpusDecoderAllocSize(2)) * coupledStreams + 0xb90c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int Align4(int value)
|
||||||
|
{
|
||||||
|
return BitUtils.AlignUp(value, 4);
|
||||||
|
}
|
||||||
|
|
||||||
private static int GetOpusDecoderSize(int channelsCount)
|
private static int GetOpusDecoderSize(int channelsCount)
|
||||||
{
|
{
|
||||||
const int silkDecoderSize = 0x2198;
|
const int SilkDecoderSize = 0x2160;
|
||||||
|
|
||||||
if (channelsCount < 1 || channelsCount > 2)
|
if (channelsCount < 1 || channelsCount > 2)
|
||||||
{
|
{
|
||||||
@@ -74,24 +183,23 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
}
|
}
|
||||||
|
|
||||||
int celtDecoderSize = GetCeltDecoderSize(channelsCount);
|
int celtDecoderSize = GetCeltDecoderSize(channelsCount);
|
||||||
|
int opusDecoderSize = GetOpusDecoderAllocSize(channelsCount) | 0x4c;
|
||||||
|
|
||||||
int opusDecoderSize = (channelsCount * 0x800 + 0x4807) & -0x800 | 0x50;
|
return opusDecoderSize + SilkDecoderSize + celtDecoderSize;
|
||||||
|
}
|
||||||
|
|
||||||
return opusDecoderSize + silkDecoderSize + celtDecoderSize;
|
private static int GetOpusDecoderAllocSize(int channelsCount)
|
||||||
|
{
|
||||||
|
return (channelsCount * 0x800 + 0x4803) & -0x800;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetCeltDecoderSize(int channelsCount)
|
private static int GetCeltDecoderSize(int channelsCount)
|
||||||
{
|
{
|
||||||
const int decodeBufferSize = 0x2030;
|
const int DecodeBufferSize = 0x2030;
|
||||||
const int celtDecoderSize = 0x58;
|
const int Overlap = 120;
|
||||||
const int celtSigSize = 0x4;
|
const int EBandsCount = 21;
|
||||||
const int overlap = 120;
|
|
||||||
const int eBandsCount = 21;
|
|
||||||
|
|
||||||
return (decodeBufferSize + overlap * 4) * channelsCount +
|
return (DecodeBufferSize + Overlap * 4) * channelsCount + EBandsCount * 16 + 0x50;
|
||||||
eBandsCount * 16 +
|
|
||||||
celtDecoderSize +
|
|
||||||
celtSigSize;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x110)]
|
||||||
|
struct OpusMultiStreamParameters
|
||||||
|
{
|
||||||
|
public int SampleRate;
|
||||||
|
public int ChannelsCount;
|
||||||
|
public int NumberOfStreams;
|
||||||
|
public int NumberOfStereoStreams;
|
||||||
|
public Array64<uint> ChannelMappings;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Audio.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x118)]
|
||||||
|
struct OpusMultiStreamParametersEx
|
||||||
|
{
|
||||||
|
public int SampleRate;
|
||||||
|
public int ChannelsCount;
|
||||||
|
public int NumberOfStreams;
|
||||||
|
public int NumberOfStereoStreams;
|
||||||
|
public OpusDecoderFlags Flags;
|
||||||
|
|
||||||
|
Array4<byte> Padding1;
|
||||||
|
|
||||||
|
public Array64<uint> ChannelMappings;
|
||||||
|
}
|
||||||
|
}
|
@@ -12,9 +12,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.Types
|
|||||||
public uint length;
|
public uint length;
|
||||||
public uint finalRange;
|
public uint finalRange;
|
||||||
|
|
||||||
public static OpusPacketHeader FromStream(BinaryReader reader)
|
public static OpusPacketHeader FromSpan(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
OpusPacketHeader header = reader.ReadStruct<OpusPacketHeader>();
|
OpusPacketHeader header = MemoryMarshal.Cast<byte, OpusPacketHeader>(data)[0];
|
||||||
|
|
||||||
header.length = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.length) : header.length;
|
header.length = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.length) : header.length;
|
||||||
header.finalRange = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.finalRange) : header.finalRange;
|
header.finalRange = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.finalRange) : header.finalRange;
|
||||||
|
@@ -7,8 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.Types
|
|||||||
struct OpusParametersEx
|
struct OpusParametersEx
|
||||||
{
|
{
|
||||||
public int SampleRate;
|
public int SampleRate;
|
||||||
public int ChannelCount;
|
public int ChannelsCount;
|
||||||
public OpusDecoderFlags UseLargeFrameSize;
|
public OpusDecoderFlags Flags;
|
||||||
|
|
||||||
Array4<byte> Padding1;
|
Array4<byte> Padding1;
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
private ControllerType[] _configuredTypes;
|
private ControllerType[] _configuredTypes;
|
||||||
private KEvent[] _styleSetUpdateEvents;
|
private KEvent[] _styleSetUpdateEvents;
|
||||||
private bool[] _supportedPlayers;
|
private bool[] _supportedPlayers;
|
||||||
private static HidVibrationValue _neutralVibrationValue = new HidVibrationValue
|
private static VibrationValue _neutralVibrationValue = new VibrationValue
|
||||||
{
|
{
|
||||||
AmplitudeLow = 0f,
|
AmplitudeLow = 0f,
|
||||||
FrequencyLow = 160f,
|
FrequencyLow = 160f,
|
||||||
@@ -33,8 +33,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
|
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
|
||||||
internal ControllerType SupportedStyleSets { get; set; }
|
internal ControllerType SupportedStyleSets { get; set; }
|
||||||
|
|
||||||
public Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>>();
|
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>>();
|
||||||
public Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)>();
|
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (VibrationValue, VibrationValue)>();
|
||||||
|
|
||||||
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
||||||
{
|
{
|
||||||
@@ -588,21 +588,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
|
WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateRumbleQueue(PlayerIndex index, Dictionary<byte, HidVibrationValue> dualVibrationValues)
|
public void UpdateRumbleQueue(PlayerIndex index, Dictionary<byte, VibrationValue> dualVibrationValues)
|
||||||
{
|
{
|
||||||
if (RumbleQueues.TryGetValue(index, out ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> currentQueue))
|
if (RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> currentQueue))
|
||||||
{
|
{
|
||||||
if (!dualVibrationValues.TryGetValue(0, out HidVibrationValue leftVibrationValue))
|
if (!dualVibrationValues.TryGetValue(0, out VibrationValue leftVibrationValue))
|
||||||
{
|
{
|
||||||
leftVibrationValue = _neutralVibrationValue;
|
leftVibrationValue = _neutralVibrationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dualVibrationValues.TryGetValue(1, out HidVibrationValue rightVibrationValue))
|
if (!dualVibrationValues.TryGetValue(1, out VibrationValue rightVibrationValue))
|
||||||
{
|
{
|
||||||
rightVibrationValue = _neutralVibrationValue;
|
rightVibrationValue = _neutralVibrationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LastVibrationValues.TryGetValue(index, out (HidVibrationValue, HidVibrationValue) dualVibrationValue) || !leftVibrationValue.Equals(dualVibrationValue.Item1) || !rightVibrationValue.Equals(dualVibrationValue.Item2))
|
if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue) || !leftVibrationValue.Equals(dualVibrationValue.Item1) || !rightVibrationValue.Equals(dualVibrationValue.Item2))
|
||||||
{
|
{
|
||||||
currentQueue.Enqueue((leftVibrationValue, rightVibrationValue));
|
currentQueue.Enqueue((leftVibrationValue, rightVibrationValue));
|
||||||
|
|
||||||
@@ -611,9 +611,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HidVibrationValue GetLastVibrationValue(PlayerIndex index, byte position)
|
public VibrationValue GetLastVibrationValue(PlayerIndex index, byte position)
|
||||||
{
|
{
|
||||||
if (!LastVibrationValues.TryGetValue(index, out (HidVibrationValue, HidVibrationValue) dualVibrationValue))
|
if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue))
|
||||||
{
|
{
|
||||||
return _neutralVibrationValue;
|
return _neutralVibrationValue;
|
||||||
}
|
}
|
||||||
@@ -621,11 +621,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
return (position == 0) ? dualVibrationValue.Item1 : dualVibrationValue.Item2;
|
return (position == 0) ? dualVibrationValue.Item1 : dualVibrationValue.Item2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> GetRumbleQueue(PlayerIndex index)
|
public ConcurrentQueue<(VibrationValue, VibrationValue)> GetRumbleQueue(PlayerIndex index)
|
||||||
{
|
{
|
||||||
if (!RumbleQueues.TryGetValue(index, out ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> rumbleQueue))
|
if (!RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> rumbleQueue))
|
||||||
{
|
{
|
||||||
rumbleQueue = new ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>();
|
rumbleQueue = new ConcurrentQueue<(VibrationValue, VibrationValue)>();
|
||||||
_device.Hid.Npads.RumbleQueues[index] = rumbleQueue;
|
_device.Hid.Npads.RumbleQueues[index] = rumbleQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,5 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
|||||||
PlayerIndex.Unknown => NpadIdType.Unknown,
|
PlayerIndex.Unknown => NpadIdType.Unknown,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(index))
|
_ => throw new ArgumentOutOfRangeException(nameof(index))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static bool IsValidNpadIdType(NpadIdType npadIdType)
|
||||||
|
{
|
||||||
|
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
public enum HidNpadJoyAssignmentMode
|
|
||||||
{
|
|
||||||
Dual,
|
|
||||||
Single
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum HidNpadHandheldActivationMode
|
public enum NpadHandheldActivationMode
|
||||||
{
|
{
|
||||||
Dual,
|
Dual,
|
||||||
Single,
|
Single,
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum HidNpadJoyDeviceType
|
public enum NpadJoyDeviceType
|
||||||
{
|
{
|
||||||
Left,
|
Left,
|
||||||
Right
|
Right
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public struct HidAccelerometerParameters
|
public struct AccelerometerParameters
|
||||||
{
|
{
|
||||||
public float X;
|
public float X;
|
||||||
public float Y;
|
public float Y;
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum HidGyroscopeZeroDriftMode
|
public enum GyroscopeZeroDriftMode
|
||||||
{
|
{
|
||||||
Loose,
|
Loose,
|
||||||
Standard,
|
Standard,
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public struct HidSensorFusionParameters
|
public struct SensorFusionParameters
|
||||||
{
|
{
|
||||||
public float RevisePower;
|
public float RevisePower;
|
||||||
public float ReviseRange;
|
public float ReviseRange;
|
@@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
public struct HidVibrationDeviceValue
|
|
||||||
{
|
|
||||||
public HidVibrationDeviceType DeviceType;
|
|
||||||
public HidVibrationDevicePosition Position;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public struct HidVibrationDeviceHandle
|
public struct VibrationDeviceHandle
|
||||||
{
|
{
|
||||||
public byte DeviceType;
|
public byte DeviceType;
|
||||||
public byte PlayerId;
|
public byte PlayerId;
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum HidVibrationDevicePosition
|
public enum VibrationDevicePosition
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Left,
|
Left,
|
@@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum HidVibrationDeviceType
|
public enum VibrationDeviceType
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
LinearResonantActuator,
|
LinearResonantActuator,
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
{
|
||||||
|
public struct VibrationDeviceValue
|
||||||
|
{
|
||||||
|
public VibrationDeviceType DeviceType;
|
||||||
|
public VibrationDevicePosition Position;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,8 @@
|
|||||||
using Ryujinx.HLE.HOS.Tamper;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public struct HidVibrationValue
|
public struct VibrationValue
|
||||||
{
|
{
|
||||||
public float AmplitudeLow;
|
public float AmplitudeLow;
|
||||||
public float FrequencyLow;
|
public float FrequencyLow;
|
||||||
@@ -12,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
return obj is HidVibrationValue value &&
|
return obj is VibrationValue value &&
|
||||||
AmplitudeLow == value.AmplitudeLow &&
|
AmplitudeLow == value.AmplitudeLow &&
|
||||||
AmplitudeHigh == value.AmplitudeHigh;
|
AmplitudeHigh == value.AmplitudeHigh;
|
||||||
}
|
}
|
@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -26,9 +27,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
private bool _isFirmwareUpdateAvailableForSixAxisSensor;
|
private bool _isFirmwareUpdateAvailableForSixAxisSensor;
|
||||||
private bool _isSixAxisSensorUnalteredPassthroughEnabled;
|
private bool _isSixAxisSensorUnalteredPassthroughEnabled;
|
||||||
|
|
||||||
private HidNpadJoyAssignmentMode _npadJoyAssignmentMode;
|
private NpadHandheldActivationMode _npadHandheldActivationMode;
|
||||||
private HidNpadHandheldActivationMode _npadHandheldActivationMode;
|
private GyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
|
||||||
private HidGyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
|
|
||||||
|
|
||||||
private long _npadCommunicationMode;
|
private long _npadCommunicationMode;
|
||||||
private uint _accelerometerPlayMode;
|
private uint _accelerometerPlayMode;
|
||||||
@@ -37,22 +37,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
private float _sevenSixAxisSensorFusionStrength;
|
private float _sevenSixAxisSensorFusionStrength;
|
||||||
|
|
||||||
private HidSensorFusionParameters _sensorFusionParams;
|
private SensorFusionParameters _sensorFusionParams;
|
||||||
private HidAccelerometerParameters _accelerometerParams;
|
private AccelerometerParameters _accelerometerParams;
|
||||||
|
|
||||||
public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer)
|
public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer)
|
||||||
{
|
{
|
||||||
_xpadIdEvent = new KEvent(context.Device.System.KernelContext);
|
_xpadIdEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
_palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext);
|
_palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
|
||||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual;
|
_npadHandheldActivationMode = NpadHandheldActivationMode.Dual;
|
||||||
_npadHandheldActivationMode = HidNpadHandheldActivationMode.Dual;
|
_gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
|
||||||
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
|
|
||||||
|
|
||||||
_isFirmwareUpdateAvailableForSixAxisSensor = false;
|
_isFirmwareUpdateAvailableForSixAxisSensor = false;
|
||||||
|
|
||||||
_sensorFusionParams = new HidSensorFusionParameters();
|
_sensorFusionParams = new SensorFusionParameters();
|
||||||
_accelerometerParams = new HidAccelerometerParameters();
|
_accelerometerParams = new AccelerometerParameters();
|
||||||
|
|
||||||
// TODO: signal event at right place
|
// TODO: signal event at right place
|
||||||
_xpadIdEvent.ReadableEvent.Signal();
|
_xpadIdEvent.ReadableEvent.Signal();
|
||||||
@@ -393,7 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||||
context.RequestData.BaseStream.Position += 4; // Padding
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
|
|
||||||
_sensorFusionParams = new HidSensorFusionParameters
|
_sensorFusionParams = new SensorFusionParameters
|
||||||
{
|
{
|
||||||
RevisePower = context.RequestData.ReadInt32(),
|
RevisePower = context.RequestData.ReadInt32(),
|
||||||
ReviseRange = context.RequestData.ReadInt32()
|
ReviseRange = context.RequestData.ReadInt32()
|
||||||
@@ -445,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||||
context.RequestData.BaseStream.Position += 4; // Padding
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
|
|
||||||
_accelerometerParams = new HidAccelerometerParameters
|
_accelerometerParams = new AccelerometerParameters
|
||||||
{
|
{
|
||||||
X = context.RequestData.ReadInt32(),
|
X = context.RequestData.ReadInt32(),
|
||||||
Y = context.RequestData.ReadInt32()
|
Y = context.RequestData.ReadInt32()
|
||||||
@@ -539,7 +538,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
|
public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||||
_gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
|
_gyroscopeZeroDriftMode = (GyroscopeZeroDriftMode)context.RequestData.ReadInt32();
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
|
||||||
@@ -570,7 +569,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
context.RequestData.BaseStream.Position += 4; // Padding
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
|
_gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
|
||||||
|
|
||||||
@@ -909,54 +908,63 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId)
|
// SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context)
|
public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context)
|
||||||
{
|
{
|
||||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single;
|
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||||
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode });
|
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(123)]
|
[CommandHipc(123)]
|
||||||
// SetNpadJoyAssignmentModeSingle(uint HidControllerId, nn::applet::AppletResourceUserId, long HidNpadJoyDeviceType)
|
// SetNpadJoyAssignmentModeSingle(uint npadIdType, nn::applet::AppletResourceUserId, uint npadJoyDeviceType)
|
||||||
public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context)
|
public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context)
|
||||||
{
|
{
|
||||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64();
|
NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadUInt32();
|
||||||
|
|
||||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single;
|
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||||
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, hidNpadJoyDeviceType, _npadJoyAssignmentMode });
|
SetNpadJoyAssignmentModeSingleWithDestinationImpl(context, npadIdType, appletResourceUserId, npadJoyDeviceType, out _, out _);
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(124)]
|
[CommandHipc(124)]
|
||||||
// SetNpadJoyAssignmentModeDual(uint HidControllerId, nn::applet::AppletResourceUserId)
|
// SetNpadJoyAssignmentModeDual(uint npadIdType, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context)
|
public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context)
|
||||||
{
|
{
|
||||||
PlayerIndex hidControllerId = HidUtils.GetIndexFromNpadIdType((NpadIdType)context.RequestData.ReadInt32());
|
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual;
|
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||||
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode });
|
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(125)]
|
[CommandHipc(125)]
|
||||||
// MergeSingleJoyAsDualJoy(uint SingleJoyId0, uint SingleJoyId1, nn::applet::AppletResourceUserId)
|
// MergeSingleJoyAsDualJoy(uint npadIdType0, uint npadIdType1, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context)
|
public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long singleJoyId0 = context.RequestData.ReadInt32();
|
NpadIdType npadIdType0 = (NpadIdType)context.RequestData.ReadUInt32();
|
||||||
long singleJoyId1 = context.RequestData.ReadInt32();
|
NpadIdType npadIdType1 = (NpadIdType)context.RequestData.ReadUInt32();
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, singleJoyId0, singleJoyId1 });
|
if (HidUtils.IsValidNpadIdType(npadIdType0) && HidUtils.IsValidNpadIdType(npadIdType1))
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, npadIdType0, npadIdType1 });
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@@ -988,7 +996,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
public ResultCode SetNpadHandheldActivationMode(ServiceCtx context)
|
public ResultCode SetNpadHandheldActivationMode(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
_npadHandheldActivationMode = (HidNpadHandheldActivationMode)context.RequestData.ReadInt64();
|
_npadHandheldActivationMode = (NpadHandheldActivationMode)context.RequestData.ReadInt64();
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode });
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode });
|
||||||
|
|
||||||
@@ -1049,35 +1057,45 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(133)] // 5.0.0+
|
[CommandHipc(133)] // 5.0.0+
|
||||||
// SetNpadJoyAssignmentModeSingleWithDestination(uint HidControllerId, long HidNpadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool Unknown0, uint Unknown1
|
// SetNpadJoyAssignmentModeSingleWithDestination(uint npadIdType, uint npadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool npadIdTypeIsSet, uint npadIdTypeSet
|
||||||
public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context)
|
public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context)
|
||||||
{
|
{
|
||||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32();
|
||||||
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64();
|
NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4; // Padding
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single;
|
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||||
|
{
|
||||||
|
SetNpadJoyAssignmentModeSingleWithDestinationImpl(context, npadIdType, appletResourceUserId, npadJoyDeviceType, out NpadIdType npadIdTypeSet, out bool npadIdTypeIsSet);
|
||||||
|
|
||||||
context.ResponseData.Write(0); //Unknown0
|
if (npadIdTypeIsSet)
|
||||||
context.ResponseData.Write(0); //Unknown1
|
{
|
||||||
|
context.ResponseData.Write(npadIdTypeIsSet);
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
|
context.ResponseData.Write((uint)npadIdTypeSet);
|
||||||
appletResourceUserId,
|
}
|
||||||
hidControllerId,
|
}
|
||||||
hidNpadJoyDeviceType,
|
|
||||||
_npadJoyAssignmentMode,
|
|
||||||
Unknown0 = 0,
|
|
||||||
Unknown1 = 0
|
|
||||||
});
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetNpadJoyAssignmentModeSingleWithDestinationImpl(ServiceCtx context, NpadIdType npadIdType, long appletResourceUserId, NpadJoyDeviceType npadJoyDeviceType, out NpadIdType npadIdTypeSet, out bool npadIdTypeIsSet)
|
||||||
|
{
|
||||||
|
npadIdTypeSet = default;
|
||||||
|
npadIdTypeIsSet = false;
|
||||||
|
|
||||||
|
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||||
|
|
||||||
|
// TODO: Service seems to use the npadJoyDeviceType to find the nearest other Npad available and merge them to dual.
|
||||||
|
// If one is found, it returns the npadIdType of the other Npad and a bool.
|
||||||
|
// If not, it returns nothing.
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(200)]
|
[CommandHipc(200)]
|
||||||
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
|
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
|
||||||
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
||||||
{
|
{
|
||||||
HidVibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<HidVibrationDeviceHandle>();
|
VibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<VibrationDeviceHandle>();
|
||||||
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
||||||
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
||||||
|
|
||||||
@@ -1093,28 +1111,28 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
return ResultCode.InvalidDeviceIndex;
|
return ResultCode.InvalidDeviceIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
HidVibrationDeviceType vibrationDeviceType = HidVibrationDeviceType.None;
|
VibrationDeviceType vibrationDeviceType = VibrationDeviceType.None;
|
||||||
|
|
||||||
if (Enum.IsDefined<NpadStyleIndex>(deviceType))
|
if (Enum.IsDefined(deviceType))
|
||||||
{
|
{
|
||||||
vibrationDeviceType = HidVibrationDeviceType.LinearResonantActuator;
|
vibrationDeviceType = VibrationDeviceType.LinearResonantActuator;
|
||||||
}
|
}
|
||||||
else if ((uint)deviceType == 8)
|
else if ((uint)deviceType == 8)
|
||||||
{
|
{
|
||||||
vibrationDeviceType = HidVibrationDeviceType.GcErm;
|
vibrationDeviceType = VibrationDeviceType.GcErm;
|
||||||
}
|
}
|
||||||
|
|
||||||
HidVibrationDevicePosition vibrationDevicePosition = HidVibrationDevicePosition.None;
|
VibrationDevicePosition vibrationDevicePosition = VibrationDevicePosition.None;
|
||||||
|
|
||||||
if (vibrationDeviceType == HidVibrationDeviceType.LinearResonantActuator)
|
if (vibrationDeviceType == VibrationDeviceType.LinearResonantActuator)
|
||||||
{
|
{
|
||||||
if (deviceHandle.Position == 0)
|
if (deviceHandle.Position == 0)
|
||||||
{
|
{
|
||||||
vibrationDevicePosition = HidVibrationDevicePosition.Left;
|
vibrationDevicePosition = VibrationDevicePosition.Left;
|
||||||
}
|
}
|
||||||
else if (deviceHandle.Position == 1)
|
else if (deviceHandle.Position == 1)
|
||||||
{
|
{
|
||||||
vibrationDevicePosition = HidVibrationDevicePosition.Right;
|
vibrationDevicePosition = VibrationDevicePosition.Right;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1122,7 +1140,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HidVibrationDeviceValue deviceInfo = new HidVibrationDeviceValue
|
VibrationDeviceValue deviceInfo = new VibrationDeviceValue
|
||||||
{
|
{
|
||||||
DeviceType = vibrationDeviceType,
|
DeviceType = vibrationDeviceType,
|
||||||
Position = vibrationDevicePosition
|
Position = vibrationDevicePosition
|
||||||
@@ -1140,7 +1158,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId)
|
// SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode SendVibrationValue(ServiceCtx context)
|
public ResultCode SendVibrationValue(ServiceCtx context)
|
||||||
{
|
{
|
||||||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
|
||||||
{
|
{
|
||||||
DeviceType = context.RequestData.ReadByte(),
|
DeviceType = context.RequestData.ReadByte(),
|
||||||
PlayerId = context.RequestData.ReadByte(),
|
PlayerId = context.RequestData.ReadByte(),
|
||||||
@@ -1148,7 +1166,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
Reserved = context.RequestData.ReadByte()
|
Reserved = context.RequestData.ReadByte()
|
||||||
};
|
};
|
||||||
|
|
||||||
HidVibrationValue vibrationValue = new HidVibrationValue
|
VibrationValue vibrationValue = new VibrationValue
|
||||||
{
|
{
|
||||||
AmplitudeLow = context.RequestData.ReadSingle(),
|
AmplitudeLow = context.RequestData.ReadSingle(),
|
||||||
FrequencyLow = context.RequestData.ReadSingle(),
|
FrequencyLow = context.RequestData.ReadSingle(),
|
||||||
@@ -1158,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
Dictionary<byte, HidVibrationValue> dualVibrationValues = new Dictionary<byte, HidVibrationValue>();
|
Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>();
|
||||||
|
|
||||||
dualVibrationValues[deviceHandle.Position] = vibrationValue;
|
dualVibrationValues[deviceHandle.Position] = vibrationValue;
|
||||||
|
|
||||||
@@ -1171,7 +1189,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue
|
// GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue
|
||||||
public ResultCode GetActualVibrationValue(ServiceCtx context)
|
public ResultCode GetActualVibrationValue(ServiceCtx context)
|
||||||
{
|
{
|
||||||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
|
||||||
{
|
{
|
||||||
DeviceType = context.RequestData.ReadByte(),
|
DeviceType = context.RequestData.ReadByte(),
|
||||||
PlayerId = context.RequestData.ReadByte(),
|
PlayerId = context.RequestData.ReadByte(),
|
||||||
@@ -1181,7 +1199,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
HidVibrationValue vibrationValue = context.Device.Hid.Npads.GetLastVibrationValue((PlayerIndex)deviceHandle.PlayerId, deviceHandle.Position);
|
VibrationValue vibrationValue = context.Device.Hid.Npads.GetLastVibrationValue((PlayerIndex)deviceHandle.PlayerId, deviceHandle.Position);
|
||||||
|
|
||||||
context.ResponseData.Write(vibrationValue.AmplitudeLow);
|
context.ResponseData.Write(vibrationValue.AmplitudeLow);
|
||||||
context.ResponseData.Write(vibrationValue.FrequencyLow);
|
context.ResponseData.Write(vibrationValue.FrequencyLow);
|
||||||
@@ -1234,12 +1252,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
|
context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
|
||||||
|
|
||||||
Span<HidVibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, HidVibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
Span<VibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, VibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
||||||
Span<HidVibrationValue> vibrationValues = MemoryMarshal.Cast<byte, HidVibrationValue>(vibrationValueBuffer);
|
Span<VibrationValue> vibrationValues = MemoryMarshal.Cast<byte, VibrationValue>(vibrationValueBuffer);
|
||||||
|
|
||||||
if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length)
|
if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length)
|
||||||
{
|
{
|
||||||
Dictionary<byte, HidVibrationValue> dualVibrationValues = new Dictionary<byte, HidVibrationValue>();
|
Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>();
|
||||||
PlayerIndex currentIndex = (PlayerIndex)deviceHandles[0].PlayerId;
|
PlayerIndex currentIndex = (PlayerIndex)deviceHandles[0].PlayerId;
|
||||||
|
|
||||||
for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++)
|
for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++)
|
||||||
@@ -1250,7 +1268,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
if (index != currentIndex || dualVibrationValues.Count == 2)
|
if (index != currentIndex || dualVibrationValues.Count == 2)
|
||||||
{
|
{
|
||||||
context.Device.Hid.Npads.UpdateRumbleQueue(currentIndex, dualVibrationValues);
|
context.Device.Hid.Npads.UpdateRumbleQueue(currentIndex, dualVibrationValues);
|
||||||
dualVibrationValues = new Dictionary<byte, HidVibrationValue>();
|
dualVibrationValues = new Dictionary<byte, VibrationValue>();
|
||||||
}
|
}
|
||||||
|
|
||||||
dualVibrationValues[position] = vibrationValues[deviceCounter];
|
dualVibrationValues[position] = vibrationValues[deviceCounter];
|
||||||
|
@@ -1,8 +1,31 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Pm
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Pm
|
||||||
{
|
{
|
||||||
[Service("pm:dmnt")]
|
[Service("pm:dmnt")]
|
||||||
class IDebugMonitorInterface : IpcService
|
class IDebugMonitorInterface : IpcService
|
||||||
{
|
{
|
||||||
public IDebugMonitorInterface(ServiceCtx context) { }
|
public IDebugMonitorInterface(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandHipc(65000)]
|
||||||
|
// AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status
|
||||||
|
public ResultCode GetProcessInfo(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong pid = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
KProcess process = KernelStatic.GetProcessByPid(pid);
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(process, out int processHandle) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new System.Exception("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(processHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -29,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||||||
private List<NroInfo> _nroInfos;
|
private List<NroInfo> _nroInfos;
|
||||||
|
|
||||||
private KProcess _owner;
|
private KProcess _owner;
|
||||||
|
private IVirtualMemoryManager _ownerMm;
|
||||||
|
|
||||||
private static Random _random = new Random();
|
private static Random _random = new Random();
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||||||
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
||||||
_nroInfos = new List<NroInfo>(MaxNro);
|
_nroInfos = new List<NroInfo>(MaxNro);
|
||||||
_owner = null;
|
_owner = null;
|
||||||
|
_ownerMm = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
|
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
|
||||||
@@ -563,12 +566,28 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||||||
return ResultCode.InvalidSession;
|
return ResultCode.InvalidSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
_owner = context.Process.HandleTable.GetKProcess(context.Request.HandleDesc.ToCopy[0]);
|
int processHandle = context.Request.HandleDesc.ToCopy[0];
|
||||||
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
_owner = context.Process.HandleTable.GetKProcess(processHandle);
|
||||||
|
_ownerMm = _owner?.CpuMemory;
|
||||||
|
context.Device.System.KernelContext.Syscall.CloseHandle(processHandle);
|
||||||
|
|
||||||
|
if (_ownerMm is IRefCounted rc)
|
||||||
|
{
|
||||||
|
rc.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(10)]
|
||||||
|
// LoadNrr2(u64, u64, u64, pid)
|
||||||
|
public ResultCode LoadNrr2(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
|
|
||||||
|
return LoadNrr(context);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (isDisposing)
|
if (isDisposing)
|
||||||
@@ -579,6 +598,11 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
_nroInfos.Clear();
|
_nroInfos.Clear();
|
||||||
|
|
||||||
|
if (_ownerMm is IRefCounted rc)
|
||||||
|
{
|
||||||
|
rc.DecrementReferenceCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Sm;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@@ -234,6 +234,17 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(62)]
|
||||||
|
// GetDebugModeFlag() -> bool
|
||||||
|
public ResultCode GetDebugModeFlag(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write(false);
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(77)]
|
[CommandHipc(77)]
|
||||||
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
|
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
|
||||||
public ResultCode GetDeviceNickName(ServiceCtx context)
|
public ResultCode GetDeviceNickName(ServiceCtx context)
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
|
||||||
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.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -17,21 +15,19 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
{
|
{
|
||||||
private static Dictionary<string, Type> _services;
|
private static Dictionary<string, Type> _services;
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, KPort> _registeredServices;
|
private readonly SmRegistry _registry;
|
||||||
|
|
||||||
private readonly ServerBase _commonServer;
|
private readonly ServerBase _commonServer;
|
||||||
|
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
|
|
||||||
public IUserInterface(KernelContext context)
|
public IUserInterface(KernelContext context, SmRegistry registry)
|
||||||
{
|
{
|
||||||
_commonServer = new ServerBase(context, "CommonServer");
|
_commonServer = new ServerBase(context, "CommonServer");
|
||||||
|
_registry = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IUserInterface()
|
static IUserInterface()
|
||||||
{
|
{
|
||||||
_registeredServices = new ConcurrentDictionary<string, KPort>();
|
|
||||||
|
|
||||||
_services = Assembly.GetExecutingAssembly().GetTypes()
|
_services = Assembly.GetExecutingAssembly().GetTypes()
|
||||||
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
|
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
|
||||||
.Select(service => (((ServiceAttribute)service).Name, type)))
|
.Select(service => (((ServiceAttribute)service).Name, type)))
|
||||||
@@ -74,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
|
|
||||||
KSession session = new KSession(context.Device.System.KernelContext);
|
KSession session = new KSession(context.Device.System.KernelContext);
|
||||||
|
|
||||||
if (_registeredServices.TryGetValue(name, out KPort port))
|
if (_registry.TryGetService(name, out KPort port))
|
||||||
{
|
{
|
||||||
KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
|
KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
|
||||||
|
|
||||||
@@ -82,6 +78,15 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
|
throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -107,7 +112,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
throw new NotImplementedException(name);
|
throw new NotImplementedException(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
|
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@@ -118,6 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
session.ClientSession.DecrementReferenceCount();
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@@ -179,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
|
|
||||||
KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0);
|
KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0);
|
||||||
|
|
||||||
if (!_registeredServices.TryAdd(name, port))
|
if (!_registry.TryRegister(name, port))
|
||||||
{
|
{
|
||||||
return ResultCode.AlreadyRegistered;
|
return ResultCode.AlreadyRegistered;
|
||||||
}
|
}
|
||||||
@@ -219,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
return ResultCode.InvalidName;
|
return ResultCode.InvalidName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_registeredServices.TryRemove(name, out _))
|
if (!_registry.Unregister(name))
|
||||||
{
|
{
|
||||||
return ResultCode.NotRegistered;
|
return ResultCode.NotRegistered;
|
||||||
}
|
}
|
||||||
|
49
Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs
Normal file
49
Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
{
|
||||||
|
class SmRegistry
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, KPort> _registeredServices;
|
||||||
|
private readonly AutoResetEvent _serviceRegistrationEvent;
|
||||||
|
|
||||||
|
public SmRegistry()
|
||||||
|
{
|
||||||
|
_registeredServices = new ConcurrentDictionary<string, KPort>();
|
||||||
|
_serviceRegistrationEvent = new AutoResetEvent(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetService(string name, out KPort port)
|
||||||
|
{
|
||||||
|
return _registeredServices.TryGetValue(name, out port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryRegister(string name, KPort port)
|
||||||
|
{
|
||||||
|
if (_registeredServices.TryAdd(name, port))
|
||||||
|
{
|
||||||
|
_serviceRegistrationEvent.Set();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unregister(string name)
|
||||||
|
{
|
||||||
|
return _registeredServices.TryRemove(name, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsServiceRegistered(string name)
|
||||||
|
{
|
||||||
|
return _registeredServices.TryGetValue(name, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitForServiceRegistration()
|
||||||
|
{
|
||||||
|
_serviceRegistrationEvent.WaitOne();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Audio.Backends.CompatLayer;
|
using Ryujinx.Audio.Backends.CompatLayer;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
@@ -48,8 +49,12 @@ namespace Ryujinx.HLE
|
|||||||
FileSystem = Configuration.VirtualFileSystem;
|
FileSystem = Configuration.VirtualFileSystem;
|
||||||
UiHandler = Configuration.HostUiHandler;
|
UiHandler = Configuration.HostUiHandler;
|
||||||
|
|
||||||
|
MemoryAllocationFlags memoryAllocationFlags = configuration.MemoryManagerMode == MemoryManagerMode.SoftwarePageTable
|
||||||
|
? MemoryAllocationFlags.Reserve
|
||||||
|
: MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable;
|
||||||
|
|
||||||
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
|
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
|
||||||
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve);
|
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
|
||||||
Gpu = new GpuContext(Configuration.GpuRenderer);
|
Gpu = new GpuContext(Configuration.GpuRenderer);
|
||||||
System = new Horizon(this);
|
System = new Horizon(this);
|
||||||
Statistics = new PerformanceStatistics();
|
Statistics = new PerformanceStatistics();
|
||||||
|
@@ -302,7 +302,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
Vector3 gyroscope = _gamepad.GetMotionData(MotionInputId.Gyroscope);
|
Vector3 gyroscope = _gamepad.GetMotionData(MotionInputId.Gyroscope);
|
||||||
|
|
||||||
accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
|
accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
|
||||||
gyroscope = new Vector3(gyroscope.X, gyroscope.Z, gyroscope.Y);
|
gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
|
||||||
|
|
||||||
_leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
|
_leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
|
||||||
|
|
||||||
@@ -543,14 +543,14 @@ namespace Ryujinx.Input.HLE
|
|||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateRumble(ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> queue)
|
public void UpdateRumble(ConcurrentQueue<(VibrationValue, VibrationValue)> queue)
|
||||||
{
|
{
|
||||||
if (queue.TryDequeue(out (HidVibrationValue, HidVibrationValue) dualVibrationValue))
|
if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
|
||||||
{
|
{
|
||||||
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
|
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
|
||||||
{
|
{
|
||||||
HidVibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
||||||
HidVibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
||||||
|
|
||||||
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
|
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
|
||||||
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
|
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
|
||||||
|
@@ -14,7 +14,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Map(ulong va, nuint hostAddress, ulong size)
|
public void Map(ulong va, ulong pa, ulong size)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -59,9 +59,9 @@ namespace Ryujinx.Memory.Tests
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<HostMemoryRange> IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size)
|
IEnumerable<MemoryRange> IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
return NoMappings ? new HostMemoryRange[0] : new HostMemoryRange[] { new HostMemoryRange((nuint)va, size) };
|
return NoMappings ? new MemoryRange[0] : new MemoryRange[] { new MemoryRange(va, size) };
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMapped(ulong va)
|
public bool IsMapped(ulong va)
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Ryujinx.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -38,5 +37,48 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
Assert.AreEqual(Marshal.ReadInt32(_memoryBlock.Pointer, 0x2040), 0xbadc0de);
|
Assert.AreEqual(Marshal.ReadInt32(_memoryBlock.Pointer, 0x2040), 0xbadc0de);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test, Explicit]
|
||||||
|
public void Test_Alias()
|
||||||
|
{
|
||||||
|
using MemoryBlock backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable);
|
||||||
|
using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
|
||||||
|
|
||||||
|
toAlias.MapView(backing, 0x1000, 0, 0x4000);
|
||||||
|
toAlias.UnmapView(backing, 0x3000, 0x1000);
|
||||||
|
|
||||||
|
toAlias.Write(0, 0xbadc0de);
|
||||||
|
Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, 0x1000), 0xbadc0de);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Explicit]
|
||||||
|
public void Test_AliasRandom()
|
||||||
|
{
|
||||||
|
using MemoryBlock backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable);
|
||||||
|
using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
|
||||||
|
|
||||||
|
Random rng = new Random(123);
|
||||||
|
|
||||||
|
for (int i = 0; i < 20000; i++)
|
||||||
|
{
|
||||||
|
int srcPage = rng.Next(0, 64);
|
||||||
|
int dstPage = rng.Next(0, 64);
|
||||||
|
int pages = rng.Next(1, 65);
|
||||||
|
|
||||||
|
if ((rng.Next() & 1) != 0)
|
||||||
|
{
|
||||||
|
toAlias.MapView(backing, (ulong)srcPage << 12, (ulong)dstPage << 12, (ulong)pages << 12);
|
||||||
|
|
||||||
|
int offset = rng.Next(0, 0x1000 - sizeof(int));
|
||||||
|
|
||||||
|
toAlias.Write((ulong)((dstPage << 12) + offset), 0xbadc0de);
|
||||||
|
Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (srcPage << 12) + offset), 0xbadc0de);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toAlias.UnmapView(backing, (ulong)dstPage << 12, (ulong)pages << 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -13,9 +13,9 @@ namespace Ryujinx.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddressSpaceManager : IVirtualMemoryManager, IWritableBlock
|
public sealed class AddressSpaceManager : IVirtualMemoryManager, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = PageTable<nuint>.PageBits;
|
public const int PageBits = PageTable<ulong>.PageBits;
|
||||||
public const int PageSize = PageTable<nuint>.PageSize;
|
public const int PageSize = PageTable<ulong>.PageSize;
|
||||||
public const int PageMask = PageTable<nuint>.PageMask;
|
public const int PageMask = PageTable<ulong>.PageMask;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Address space width in bits.
|
/// Address space width in bits.
|
||||||
@@ -24,14 +24,15 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
private readonly ulong _addressSpaceSize;
|
private readonly ulong _addressSpaceSize;
|
||||||
|
|
||||||
private readonly PageTable<nuint> _pageTable;
|
private readonly MemoryBlock _backingMemory;
|
||||||
|
private readonly PageTable<ulong> _pageTable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the memory manager.
|
/// Creates a new instance of the memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
||||||
/// <param name="addressSpaceSize">Size of the address space</param>
|
/// <param name="addressSpaceSize">Size of the address space</param>
|
||||||
public AddressSpaceManager(ulong addressSpaceSize)
|
public AddressSpaceManager(MemoryBlock backingMemory, ulong addressSpaceSize)
|
||||||
{
|
{
|
||||||
ulong asSize = PageSize;
|
ulong asSize = PageSize;
|
||||||
int asBits = PageBits;
|
int asBits = PageBits;
|
||||||
@@ -44,37 +45,26 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
AddressSpaceBits = asBits;
|
AddressSpaceBits = asBits;
|
||||||
_addressSpaceSize = asSize;
|
_addressSpaceSize = asSize;
|
||||||
_pageTable = new PageTable<nuint>();
|
_backingMemory = backingMemory;
|
||||||
|
_pageTable = new PageTable<ulong>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Maps a virtual memory range into a physical memory range.
|
public void Map(ulong va, ulong pa, ulong size)
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Addresses and size must be page aligned.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="va">Virtual memory address</param>
|
|
||||||
/// <param name="hostAddress">Physical memory address</param>
|
|
||||||
/// <param name="size">Size to be mapped</param>
|
|
||||||
public void Map(ulong va, nuint hostAddress, ulong size)
|
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
while (size != 0)
|
while (size != 0)
|
||||||
{
|
{
|
||||||
_pageTable.Map(va, hostAddress);
|
_pageTable.Map(va, pa);
|
||||||
|
|
||||||
va += PageSize;
|
va += PageSize;
|
||||||
hostAddress += PageSize;
|
pa += PageSize;
|
||||||
size -= PageSize;
|
size -= PageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Unmaps a previously mapped range of virtual memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the range to be unmapped</param>
|
|
||||||
/// <param name="size">Size of the range to be unmapped</param>
|
|
||||||
public void Unmap(ulong va, ulong size)
|
public void Unmap(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
@@ -88,47 +78,25 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Reads data from mapped memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data being read</typeparam>
|
|
||||||
/// <param name="va">Virtual address of the data in memory</param>
|
|
||||||
/// <returns>The data</returns>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
|
||||||
public T Read<T>(ulong va) where T : unmanaged
|
public T Read<T>(ulong va) where T : unmanaged
|
||||||
{
|
{
|
||||||
return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
|
return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Reads data from mapped memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the data in memory</param>
|
|
||||||
/// <param name="data">Span to store the data being read into</param>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
|
||||||
public void Read(ulong va, Span<byte> data)
|
public void Read(ulong va, Span<byte> data)
|
||||||
{
|
{
|
||||||
ReadImpl(va, data);
|
ReadImpl(va, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Writes data to mapped memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data being written</typeparam>
|
|
||||||
/// <param name="va">Virtual address to write the data into</param>
|
|
||||||
/// <param name="value">Data to be written</param>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
|
||||||
public void Write<T>(ulong va, T value) where T : unmanaged
|
public void Write<T>(ulong va, T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
|
Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Writes data to mapped memory.
|
|
||||||
/// </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, ReadOnlySpan<byte> data)
|
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
if (data.Length == 0)
|
if (data.Length == 0)
|
||||||
@@ -140,7 +108,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (IsContiguousAndMapped(va, data.Length))
|
if (IsContiguousAndMapped(va, data.Length))
|
||||||
{
|
{
|
||||||
data.CopyTo(GetHostSpanContiguous(va, data.Length));
|
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -148,34 +116,27 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size));
|
data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, PageSize);
|
size = Math.Min(data.Length - offset, PageSize);
|
||||||
|
|
||||||
data.Slice(offset, size).CopyTo(GetHostSpanContiguous(va + (ulong)offset, size));
|
data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets a read-only span of data from mapped memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This may perform a allocation if the data is not contiguous in memory.
|
|
||||||
/// For this reason, the span is read-only, you can't modify the data.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="va">Virtual address of the data</param>
|
|
||||||
/// <param name="size">Size of the data</param>
|
|
||||||
/// <param name="tracked">True if read tracking is triggered on the span</param>
|
|
||||||
/// <returns>A read-only span of the data</returns>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
|
||||||
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@@ -185,7 +146,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (IsContiguousAndMapped(va, size))
|
if (IsContiguousAndMapped(va, size))
|
||||||
{
|
{
|
||||||
return GetHostSpanContiguous(va, size);
|
return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -197,19 +158,7 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets a region of memory that can be written to.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the requested region is not contiguous in physical memory,
|
|
||||||
/// this will perform an allocation, and flush the data (writing it
|
|
||||||
/// back to the backing memory) on disposal.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="va">Virtual address of the data</param>
|
|
||||||
/// <param name="size">Size of the data</param>
|
|
||||||
/// <param name="tracked">True if write tracking is triggered on the span</param>
|
|
||||||
/// <returns>A writable region of memory containing the data</returns>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
|
||||||
public unsafe WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
|
public unsafe WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@@ -219,7 +168,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (IsContiguousAndMapped(va, size))
|
if (IsContiguousAndMapped(va, size))
|
||||||
{
|
{
|
||||||
return new WritableRegion(null, va, new NativeMemoryManager<byte>((byte*)GetHostAddress(va), size).Memory);
|
return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -231,33 +180,18 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets a reference for the given type at the specified virtual memory address.
|
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The data must be located at a contiguous memory region.
|
|
||||||
/// </remarks>
|
|
||||||
/// <typeparam name="T">Type of the data to get the reference</typeparam>
|
|
||||||
/// <param name="va">Virtual address of the data</param>
|
|
||||||
/// <returns>A reference to the data in memory</returns>
|
|
||||||
/// <exception cref="MemoryNotContiguousException">Throw if the specified memory region is not contiguous in physical memory</exception>
|
|
||||||
public unsafe ref T GetRef<T>(ulong va) where T : unmanaged
|
|
||||||
{
|
{
|
||||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||||
{
|
{
|
||||||
ThrowMemoryNotContiguous();
|
ThrowMemoryNotContiguous();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref *(T*)GetHostAddress(va);
|
return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Computes the number of pages in a virtual address range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the range</param>
|
|
||||||
/// <param name="size">Size of the range</param>
|
|
||||||
/// <param name="startVa">The virtual address of the beginning of the first page</param>
|
|
||||||
/// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int GetPagesCount(ulong va, uint size, out ulong startVa)
|
private int GetPagesCount(ulong va, uint size, out ulong startVa)
|
||||||
{
|
{
|
||||||
@@ -268,7 +202,7 @@ namespace Ryujinx.Memory
|
|||||||
return (int)(vaSpan / PageSize);
|
return (int)(vaSpan / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
|
private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
|
private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
|
||||||
@@ -290,7 +224,7 @@ namespace Ryujinx.Memory
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize))
|
if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -301,18 +235,12 @@ namespace Ryujinx.Memory
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the physical regions that make up the given virtual address region.
|
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||||
/// If any part of the virtual region is unmapped, null is returned.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the range</param>
|
|
||||||
/// <param name="size">Size of the range</param>
|
|
||||||
/// <returns>Array of physical regions</returns>
|
|
||||||
public IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<HostMemoryRange>();
|
return Enumerable.Empty<MemoryRange>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||||
@@ -322,9 +250,9 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
var regions = new List<HostMemoryRange>();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
nuint regionStart = GetHostAddress(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
@@ -334,12 +262,12 @@ namespace Ryujinx.Memory
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nuint newHostAddress = GetHostAddress(va + PageSize);
|
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||||
|
|
||||||
if (GetHostAddress(va) + PageSize != newHostAddress)
|
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new HostMemoryRange(regionStart, regionSize));
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
regionStart = newHostAddress;
|
regionStart = newPa;
|
||||||
regionSize = 0;
|
regionSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,7 +275,7 @@ namespace Ryujinx.Memory
|
|||||||
regionSize += PageSize;
|
regionSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.Add(new HostMemoryRange(regionStart, regionSize));
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
|
|
||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
@@ -365,26 +293,26 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size));
|
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size));
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
|
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, PageSize);
|
size = Math.Min(data.Length - offset, PageSize);
|
||||||
|
|
||||||
GetHostSpanContiguous(va + (ulong)offset, size).CopyTo(data.Slice(offset, size));
|
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Checks if the page at a given virtual address is mapped.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address to check</param>
|
|
||||||
/// <returns>True if the address is mapped, false otherwise</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool IsMapped(ulong va)
|
public bool IsMapped(ulong va)
|
||||||
{
|
{
|
||||||
@@ -396,12 +324,7 @@ namespace Ryujinx.Memory
|
|||||||
return _pageTable.Read(va) != 0;
|
return _pageTable.Read(va) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Checks if a memory range is mapped.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="va">Virtual address of the range</param>
|
|
||||||
/// <param name="size">Size of the range in bytes</param>
|
|
||||||
/// <returns>True if the entire range is mapped, false otherwise</returns>
|
|
||||||
public bool IsRangeMapped(ulong va, ulong size)
|
public bool IsRangeMapped(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (size == 0UL)
|
if (size == 0UL)
|
||||||
@@ -460,14 +383,9 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe Span<byte> GetHostSpanContiguous(ulong va, int size)
|
private ulong GetPhysicalAddressInternal(ulong va)
|
||||||
{
|
{
|
||||||
return new Span<byte>((void*)GetHostAddress(va), size);
|
return _pageTable.Read(va) + (va & PageMask);
|
||||||
}
|
|
||||||
|
|
||||||
private nuint GetHostAddress(ulong va)
|
|
||||||
{
|
|
||||||
return _pageTable.Read(va) + (nuint)(va & PageMask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -13,9 +13,9 @@ namespace Ryujinx.Memory
|
|||||||
/// Addresses and size must be page aligned.
|
/// Addresses and size must be page aligned.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="va">Virtual memory address</param>
|
/// <param name="va">Virtual memory address</param>
|
||||||
/// <param name="hostAddress">Pointer where the region should be mapped to</param>
|
/// <param name="pa">Physical memory address where the region should be mapped to</param>
|
||||||
/// <param name="size">Size to be mapped</param>
|
/// <param name="size">Size to be mapped</param>
|
||||||
void Map(ulong va, nuint hostAddress, ulong size);
|
void Map(ulong va, ulong pa, ulong size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unmaps a previously mapped range of virtual memory.
|
/// Unmaps a previously mapped range of virtual memory.
|
||||||
@@ -111,7 +111,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <param name="va">Virtual address of the range</param>
|
/// <param name="va">Virtual address of the range</param>
|
||||||
/// <param name="size">Size of the range</param>
|
/// <param name="size">Size of the range</param>
|
||||||
/// <returns>Array of physical regions</returns>
|
/// <returns>Array of physical regions</returns>
|
||||||
IEnumerable<HostMemoryRange> GetPhysicalRegions(ulong va, ulong size);
|
IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the page at a given CPU virtual address is mapped.
|
/// Checks if the page at a given CPU virtual address is mapped.
|
||||||
|
@@ -29,6 +29,18 @@ namespace Ryujinx.Memory
|
|||||||
/// Enables mirroring of the memory block through aliasing of memory pages.
|
/// Enables mirroring of the memory block through aliasing of memory pages.
|
||||||
/// When enabled, this allows creating more memory blocks sharing the same backing storage.
|
/// When enabled, this allows creating more memory blocks sharing the same backing storage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Mirrorable = 1 << 2
|
Mirrorable = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the memory block should support mapping views of a mirrorable memory block.
|
||||||
|
/// The block that is to have their views mapped should be created with the <see cref="Mirrorable"/> flag.
|
||||||
|
/// </summary>
|
||||||
|
ViewCompatible = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces views to be mapped page by page on Windows. When partial unmaps are done, this avoids the need
|
||||||
|
/// to unmap the full range and remap sub-ranges, which creates a time window with incorrectly unmapped memory.
|
||||||
|
/// </summary>
|
||||||
|
ForceWindows4KBViewMapping = 1 << 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -11,8 +12,12 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
private readonly bool _usesSharedMemory;
|
private readonly bool _usesSharedMemory;
|
||||||
private readonly bool _isMirror;
|
private readonly bool _isMirror;
|
||||||
|
private readonly bool _viewCompatible;
|
||||||
|
private readonly bool _forceWindows4KBView;
|
||||||
private IntPtr _sharedMemory;
|
private IntPtr _sharedMemory;
|
||||||
private IntPtr _pointer;
|
private IntPtr _pointer;
|
||||||
|
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
|
||||||
|
private int _viewCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pointer to the memory block data.
|
/// Pointer to the memory block data.
|
||||||
@@ -36,12 +41,14 @@ namespace Ryujinx.Memory
|
|||||||
if (flags.HasFlag(MemoryAllocationFlags.Mirrorable))
|
if (flags.HasFlag(MemoryAllocationFlags.Mirrorable))
|
||||||
{
|
{
|
||||||
_sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve));
|
_sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve));
|
||||||
_pointer = MemoryManagement.MapSharedMemory(_sharedMemory);
|
_pointer = MemoryManagement.MapSharedMemory(_sharedMemory, size);
|
||||||
_usesSharedMemory = true;
|
_usesSharedMemory = true;
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(MemoryAllocationFlags.Reserve))
|
else if (flags.HasFlag(MemoryAllocationFlags.Reserve))
|
||||||
{
|
{
|
||||||
_pointer = MemoryManagement.Reserve(size);
|
_viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible);
|
||||||
|
_forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping);
|
||||||
|
_pointer = MemoryManagement.Reserve(size, _viewCompatible, _forceWindows4KBView);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -49,6 +56,10 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
|
_viewStorages = new ConcurrentDictionary<MemoryBlock, byte>();
|
||||||
|
_viewStorages.TryAdd(this, 0);
|
||||||
|
_viewCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -60,7 +71,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
|
/// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
|
||||||
private MemoryBlock(ulong size, IntPtr sharedMemory)
|
private MemoryBlock(ulong size, IntPtr sharedMemory)
|
||||||
{
|
{
|
||||||
_pointer = MemoryManagement.MapSharedMemory(sharedMemory);
|
_pointer = MemoryManagement.MapSharedMemory(sharedMemory, size);
|
||||||
Size = size;
|
Size = size;
|
||||||
_usesSharedMemory = true;
|
_usesSharedMemory = true;
|
||||||
_isMirror = true;
|
_isMirror = true;
|
||||||
@@ -112,6 +123,42 @@ namespace Ryujinx.Memory
|
|||||||
return MemoryManagement.Decommit(GetPointerInternal(offset, size), size);
|
return MemoryManagement.Decommit(GetPointerInternal(offset, size), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a view of memory from another memory block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcBlock">Memory block from where the backing memory will be taken</param>
|
||||||
|
/// <param name="srcOffset">Offset on <paramref name="srcBlock"/> of the region that should be mapped</param>
|
||||||
|
/// <param name="dstOffset">Offset to map the view into on this block</param>
|
||||||
|
/// <param name="size">Size of the range to be mapped</param>
|
||||||
|
/// <exception cref="NotSupportedException">Throw when the source memory block does not support mirroring</exception>
|
||||||
|
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||||
|
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||||
|
public void MapView(MemoryBlock srcBlock, ulong srcOffset, ulong dstOffset, ulong size)
|
||||||
|
{
|
||||||
|
if (srcBlock._sharedMemory == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_viewStorages.TryAdd(srcBlock, 0))
|
||||||
|
{
|
||||||
|
srcBlock.IncrementViewCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unmaps a view of memory from another memory block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcBlock">Memory block from where the backing memory was taken during map</param>
|
||||||
|
/// <param name="offset">Offset of the view previously mapped with <see cref="MapView"/></param>
|
||||||
|
/// <param name="size">Size of the range to be unmapped</param>
|
||||||
|
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
|
||||||
|
{
|
||||||
|
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reprotects a region of memory.
|
/// Reprotects a region of memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -124,21 +171,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
||||||
public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true)
|
public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true)
|
||||||
{
|
{
|
||||||
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, throwOnFail);
|
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, _forceWindows4KBView, throwOnFail);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remaps a region of memory into this memory block.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Starting offset of the range to be remapped into</param>
|
|
||||||
/// <param name="sourceAddress">Starting offset of the range to be remapped from</param>
|
|
||||||
/// <param name="size">Size of the range to be remapped</param>
|
|
||||||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
|
||||||
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
|
||||||
public void Remap(ulong offset, IntPtr sourceAddress, ulong size)
|
|
||||||
{
|
|
||||||
MemoryManagement.Remap(GetPointerInternal(offset, size), sourceAddress, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -274,7 +307,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public nuint GetPointer(ulong offset, ulong size) => (nuint)(ulong)GetPointerInternal(offset, size);
|
public IntPtr GetPointer(ulong offset, ulong size) => GetPointerInternal(offset, size);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private IntPtr GetPointerInternal(ulong offset, ulong size)
|
private IntPtr GetPointerInternal(ulong offset, ulong size)
|
||||||
@@ -367,22 +400,63 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
if (_usesSharedMemory)
|
if (_usesSharedMemory)
|
||||||
{
|
{
|
||||||
MemoryManagement.UnmapSharedMemory(ptr);
|
MemoryManagement.UnmapSharedMemory(ptr, Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemoryManagement.Free(ptr, Size, _forceWindows4KBView);
|
||||||
|
}
|
||||||
|
|
||||||
if (_sharedMemory != IntPtr.Zero && !_isMirror)
|
foreach (MemoryBlock viewStorage in _viewStorages.Keys)
|
||||||
|
{
|
||||||
|
viewStorage.DecrementViewCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewStorages.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the number of views that uses this memory block as storage.
|
||||||
|
/// </summary>
|
||||||
|
private void IncrementViewCount()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _viewCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrements the number of views that uses this memory block as storage.
|
||||||
|
/// </summary>
|
||||||
|
private void DecrementViewCount()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref _viewCount) == 0 && _sharedMemory != IntPtr.Zero && !_isMirror)
|
||||||
{
|
{
|
||||||
MemoryManagement.DestroySharedMemory(_sharedMemory);
|
MemoryManagement.DestroySharedMemory(_sharedMemory);
|
||||||
_sharedMemory = IntPtr.Zero;
|
_sharedMemory = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the specified memory allocation flags are supported on the current platform.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flags">Flags to be checked</param>
|
||||||
|
/// <returns>True if the platform supports all the flags, false otherwise</returns>
|
||||||
|
public static bool SupportsFlags(MemoryAllocationFlags flags)
|
||||||
{
|
{
|
||||||
MemoryManagement.Free(ptr);
|
if (flags.HasFlag(MemoryAllocationFlags.ViewCompatible))
|
||||||
}
|
{
|
||||||
}
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
return OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
|
return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS();
|
||||||
private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||||
|
private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,12 +8,9 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
return MemoryManagementWindows.Allocate((IntPtr)size);
|
||||||
|
|
||||||
return MemoryManagementWindows.Allocate(sizeNint);
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.Allocate(size);
|
return MemoryManagementUnix.Allocate(size);
|
||||||
}
|
}
|
||||||
@@ -23,16 +20,13 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IntPtr Reserve(ulong size)
|
public static IntPtr Reserve(ulong size, bool viewCompatible, bool force4KBMap)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible, force4KBMap);
|
||||||
|
|
||||||
return MemoryManagementWindows.Reserve(sizeNint);
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.Reserve(size);
|
return MemoryManagementUnix.Reserve(size);
|
||||||
}
|
}
|
||||||
@@ -46,12 +40,9 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
return MemoryManagementWindows.Commit(address, (IntPtr)size);
|
||||||
|
|
||||||
return MemoryManagementWindows.Commit(address, sizeNint);
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.Commit(address, size);
|
return MemoryManagementUnix.Commit(address, size);
|
||||||
}
|
}
|
||||||
@@ -65,12 +56,9 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
return MemoryManagementWindows.Decommit(address, (IntPtr)size);
|
||||||
|
|
||||||
return MemoryManagementWindows.Decommit(address, sizeNint);
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.Decommit(address, size);
|
return MemoryManagementUnix.Decommit(address, size);
|
||||||
}
|
}
|
||||||
@@ -80,18 +68,68 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool throwOnFail)
|
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, bool force4KBMap)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
if (force4KBMap)
|
||||||
|
{
|
||||||
|
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
MemoryManagementUnix.MapView(sharedMemory, srcOffset, address, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, bool force4KBMap)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
if (force4KBMap)
|
||||||
|
{
|
||||||
|
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
MemoryManagementUnix.UnmapView(address, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool forView, bool force4KBMap, bool throwOnFail)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
if (forView && force4KBMap)
|
||||||
|
{
|
||||||
result = MemoryManagementWindows.Reprotect(address, sizeNint, permission);
|
result = MemoryManagementWindows.Reprotect4KB(address, (IntPtr)size, permission, forView);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else
|
||||||
OperatingSystem.IsMacOS())
|
{
|
||||||
|
result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
result = MemoryManagementUnix.Reprotect(address, size, permission);
|
result = MemoryManagementUnix.Reprotect(address, size, permission);
|
||||||
}
|
}
|
||||||
@@ -106,14 +144,13 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Free(IntPtr address)
|
public static bool Free(IntPtr address, ulong size, bool force4KBMap)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
return MemoryManagementWindows.Free(address);
|
return MemoryManagementWindows.Free(address, (IntPtr)size, force4KBMap);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.Free(address);
|
return MemoryManagementUnix.Free(address);
|
||||||
}
|
}
|
||||||
@@ -127,12 +164,9 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
IntPtr sizeNint = new IntPtr((long)size);
|
return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve);
|
||||||
|
|
||||||
return MemoryManagementWindows.CreateSharedMemory(sizeNint, reserve);
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.CreateSharedMemory(size, reserve);
|
return MemoryManagementUnix.CreateSharedMemory(size, reserve);
|
||||||
}
|
}
|
||||||
@@ -148,8 +182,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.DestroySharedMemory(handle);
|
MemoryManagementWindows.DestroySharedMemory(handle);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
MemoryManagementUnix.DestroySharedMemory(handle);
|
MemoryManagementUnix.DestroySharedMemory(handle);
|
||||||
}
|
}
|
||||||
@@ -159,16 +192,15 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IntPtr MapSharedMemory(IntPtr handle)
|
public static IntPtr MapSharedMemory(IntPtr handle, ulong size)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
return MemoryManagementWindows.MapSharedMemory(handle);
|
return MemoryManagementWindows.MapSharedMemory(handle);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
return MemoryManagementUnix.MapSharedMemory(handle);
|
return MemoryManagementUnix.MapSharedMemory(handle, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -176,29 +208,15 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnmapSharedMemory(IntPtr address)
|
public static void UnmapSharedMemory(IntPtr address, ulong size)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
MemoryManagementWindows.UnmapSharedMemory(address);
|
MemoryManagementWindows.UnmapSharedMemory(address);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() ||
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
MemoryManagementUnix.UnmapSharedMemory(address);
|
MemoryManagementUnix.UnmapSharedMemory(address, size);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr Remap(IntPtr target, IntPtr source, ulong size)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux() ||
|
|
||||||
OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
return MemoryManagementUnix.Remap(target, source, size);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user