Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c05c688ee8 | ||
|
b2623dc27d | ||
|
5131b71437 | ||
|
7870423671 | ||
|
b72916fbc1 | ||
|
da073fce61 |
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||||
{
|
{
|
||||||
_stream = new MemoryStream();
|
_stream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
AllocResult = allocResult;
|
AllocResult = allocResult;
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -1285,7 +1286,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
using var codeStream = new MemoryStream();
|
using var codeStream = MemoryStreamManager.Shared.GetStream();
|
||||||
var assembler = new Assembler(codeStream, HasRelocs);
|
var assembler = new Assembler(codeStream, HasRelocs);
|
||||||
|
|
||||||
bool hasRelocs = HasRelocs;
|
bool hasRelocs = HasRelocs;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||||
{
|
{
|
||||||
_stream = new MemoryStream();
|
_stream = MemoryStreamManager.Shared.GetStream();
|
||||||
_blockLabels = new Operand[blocksCount];
|
_blockLabels = new Operand[blocksCount];
|
||||||
|
|
||||||
AllocResult = allocResult;
|
AllocResult = allocResult;
|
||||||
|
@@ -6,6 +6,7 @@ using ARMeilleure.Memory;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -150,10 +151,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private void InitializeCarriers()
|
private void InitializeCarriers()
|
||||||
{
|
{
|
||||||
_infosStream = new MemoryStream();
|
_infosStream = MemoryStreamManager.Shared.GetStream();
|
||||||
_codesList = new List<byte[]>();
|
_codesList = new List<byte[]>();
|
||||||
_relocsStream = new MemoryStream();
|
_relocsStream = MemoryStreamManager.Shared.GetStream();
|
||||||
_unwindInfosStream = new MemoryStream();
|
_unwindInfosStream = MemoryStreamManager.Shared.GetStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeCarriers()
|
private void DisposeCarriers()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -182,7 +183,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||||
|
|
||||||
@@ -274,7 +275,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
outerHeader.SetHeaderHash();
|
outerHeader.SetHeaderHash();
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||||
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -12,19 +12,5 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
|
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
|
||||||
|
|
||||||
writer.Write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Write(this BinaryWriter writer, UInt128 value)
|
|
||||||
{
|
|
||||||
writer.Write((ulong)value);
|
|
||||||
writer.Write((ulong)(value >> 64));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
Normal file
28
Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class BinaryWriterExtensions
|
||||||
|
{
|
||||||
|
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
||||||
|
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this BinaryWriter writer, UInt128 value)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)value);
|
||||||
|
writer.Write((ulong)(value >> 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this BinaryWriter writer, MemoryStream stream)
|
||||||
|
{
|
||||||
|
stream.CopyTo(writer.BaseStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
Ryujinx.Common/Extensions/StreamExtensions.cs
Normal file
138
Ryujinx.Common/Extensions/StreamExtensions.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class StreamExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a <cref="ReadOnlySpan<int>" /> to this stream.
|
||||||
|
///
|
||||||
|
/// This default implementation converts each buffer value to a stack-allocated
|
||||||
|
/// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="buffer">The buffer of values to be written</param>
|
||||||
|
public static void Write(this Stream stream, ReadOnlySpan<int> buffer)
|
||||||
|
{
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer);
|
||||||
|
stream.Write(byteBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span<byte> byteBuffer = stackalloc byte[sizeof(int)];
|
||||||
|
|
||||||
|
foreach (int value in buffer)
|
||||||
|
{
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value);
|
||||||
|
stream.Write(byteBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a four-byte signed integer to this stream. The current position
|
||||||
|
/// of the stream is advanced by four.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, int value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(int)];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes an eight-byte signed integer to this stream. The current position
|
||||||
|
/// of the stream is advanced by eight.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, long value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(long)];
|
||||||
|
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
// Writes a four-byte unsigned integer to this stream. The current position
|
||||||
|
// of the stream is advanced by four.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, uint value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes an eight-byte unsigned integer to this stream. The current
|
||||||
|
/// position of the stream is advanced by eight.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="value">The value to be written</param>
|
||||||
|
public static void Write(this Stream stream, ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||||
|
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
||||||
|
stream.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the contents of source to stream by calling source.CopyTo(stream).
|
||||||
|
/// Provides consistency with other Stream.Write methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to</param>
|
||||||
|
/// <param name="source">The stream to be read from</param>
|
||||||
|
public static void Write(this Stream stream, Stream source)
|
||||||
|
{
|
||||||
|
source.CopyTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a sequence of bytes to the Stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to be written to.</param>
|
||||||
|
/// <param name="value">The byte to be written</param>
|
||||||
|
/// <param name="count">The number of times the value should be written</param>
|
||||||
|
public static void WriteByte(this Stream stream, byte value, int count)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int BlockSize = 16;
|
||||||
|
|
||||||
|
int blockCount = count / BlockSize;
|
||||||
|
if (blockCount > 0)
|
||||||
|
{
|
||||||
|
Span<byte> span = stackalloc byte[BlockSize];
|
||||||
|
span.Fill(value);
|
||||||
|
for (int x = 0; x < blockCount; x++)
|
||||||
|
{
|
||||||
|
stream.Write(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nonBlockBytes = count % BlockSize;
|
||||||
|
for (int x = 0; x < nonBlockBytes; x++)
|
||||||
|
{
|
||||||
|
stream.WriteByte(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
Ryujinx.Common/Memory/MemoryStreamManager.cs
Normal file
99
Ryujinx.Common/Memory/MemoryStreamManager.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public static class MemoryStreamManager
|
||||||
|
{
|
||||||
|
private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
|
||||||
|
/// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
|
||||||
|
/// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast.
|
||||||
|
/// </summary>
|
||||||
|
public static class Shared
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream()
|
||||||
|
=> new RecyclableMemoryStream(_shared);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(byte[] buffer)
|
||||||
|
=> GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer)
|
||||||
|
=> GetStream(Guid.NewGuid(), null, buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
|
||||||
|
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
|
||||||
|
/// <param name="tag">A tag which can be used to track the source of the stream</param>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
RecyclableMemoryStream stream = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
|
||||||
|
stream.Write(buffer);
|
||||||
|
stream.Position = 0;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
|
||||||
|
/// buffer. The provided buffer is not wrapped or used after construction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks>
|
||||||
|
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
|
||||||
|
/// <param name="tag">A tag which can be used to track the source of the stream</param>
|
||||||
|
/// <param name="buffer">The byte buffer to copy data from</param>
|
||||||
|
/// <param name="offset">The offset from the start of the buffer to copy from</param>
|
||||||
|
/// <param name="count">The number of bytes to copy from the buffer</param>
|
||||||
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
|
public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
RecyclableMemoryStream stream = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new RecyclableMemoryStream(_shared, id, tag, count);
|
||||||
|
stream.Write(buffer, offset, count);
|
||||||
|
stream.Position = 0;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="System.Management" />
|
<PackageReference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -38,12 +40,7 @@ namespace Ryujinx.Common
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var mem = new MemoryStream())
|
return StreamUtils.StreamToBytes(stream);
|
||||||
{
|
|
||||||
stream.CopyTo(mem);
|
|
||||||
|
|
||||||
return mem.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +53,7 @@ namespace Ryujinx.Common
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var mem = new MemoryStream())
|
return await StreamUtils.StreamToBytesAsync(stream);
|
||||||
{
|
|
||||||
await stream.CopyToAsync(mem);
|
|
||||||
|
|
||||||
return mem.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
using System.IO;
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
@@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
public static byte[] StreamToBytes(Stream input)
|
public static byte[] StreamToBytes(Stream input)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
input.CopyTo(stream);
|
input.CopyTo(stream);
|
||||||
|
|
||||||
return stream.ToArray();
|
return stream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
|
{
|
||||||
|
await input.CopyToAsync(stream, cancellationToken);
|
||||||
|
|
||||||
|
return stream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
using Ryujinx.Memory;
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -40,7 +42,7 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
|
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
|
||||||
{
|
{
|
||||||
@@ -54,7 +56,7 @@ namespace Ryujinx.Cpu
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.ASCII.GetString(ms.ToArray());
|
return Encoding.ASCII.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
texture._viewStorage = this;
|
texture._viewStorage = this;
|
||||||
|
|
||||||
Group.UpdateViews(_views);
|
Group.UpdateViews(_views, texture);
|
||||||
|
|
||||||
if (texture.Group != null && texture.Group != Group)
|
if (texture.Group != null && texture.Group != Group)
|
||||||
{
|
{
|
||||||
@@ -384,6 +384,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_views.Remove(texture);
|
_views.Remove(texture);
|
||||||
|
|
||||||
|
Group.RemoveView(texture);
|
||||||
|
|
||||||
texture._viewStorage = texture;
|
texture._viewStorage = texture;
|
||||||
|
|
||||||
DecrementReferenceCount();
|
DecrementReferenceCount();
|
||||||
|
@@ -989,7 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Update the views in this texture group, rebuilding the memory tracking if required.
|
/// Update the views in this texture group, rebuilding the memory tracking if required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="views">The views list of the storage texture</param>
|
/// <param name="views">The views list of the storage texture</param>
|
||||||
public void UpdateViews(List<Texture> views)
|
/// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
|
||||||
|
public void UpdateViews(List<Texture> views, Texture texture)
|
||||||
{
|
{
|
||||||
// This is saved to calculate overlapping views for each handle.
|
// This is saved to calculate overlapping views for each handle.
|
||||||
_views = views;
|
_views = views;
|
||||||
@@ -1027,17 +1028,44 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (!regionsRebuilt)
|
if (!regionsRebuilt)
|
||||||
{
|
{
|
||||||
// Must update the overlapping views on all handles, but only if they were not just recreated.
|
if (texture != null)
|
||||||
|
|
||||||
foreach (TextureGroupHandle handle in _handles)
|
|
||||||
{
|
{
|
||||||
handle.RecalculateOverlaps(this, views);
|
int offset = FindOffset(texture);
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.AddOverlap(offset, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Must update the overlapping views on all handles, but only if they were not just recreated.
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.RecalculateOverlaps(this, views);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalAllDirty();
|
SignalAllDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a view from the group, removing it from all overlap lists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="view">View to remove from the group</param>
|
||||||
|
public void RemoveView(Texture view)
|
||||||
|
{
|
||||||
|
int offset = FindOffset(view);
|
||||||
|
|
||||||
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
|
{
|
||||||
|
handle.RemoveOverlap(offset, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inherit handle state from an old set of handles, such as modified and dirty flags.
|
/// Inherit handle state from an old set of handles, such as modified and dirty flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -159,6 +159,42 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a single texture view as an overlap if its range overlaps.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The offset of the view in the group</param>
|
||||||
|
/// <param name="view">The texture to add as an overlap</param>
|
||||||
|
public void AddOverlap(int offset, Texture view)
|
||||||
|
{
|
||||||
|
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
||||||
|
|
||||||
|
if (OverlapsWith(offset, (int)view.Size))
|
||||||
|
{
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
Overlaps.Add(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a single texture view as an overlap if its range overlaps.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The offset of the view in the group</param>
|
||||||
|
/// <param name="view">The texture to add as an overlap</param>
|
||||||
|
public void RemoveOverlap(int offset, Texture view)
|
||||||
|
{
|
||||||
|
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
||||||
|
|
||||||
|
if (OverlapsWith(offset, (int)view.Size))
|
||||||
|
{
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
Overlaps.Remove(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
|
/// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
@@ -11,16 +13,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
public static byte[] Pack(ShaderSource[] sources)
|
public static byte[] Pack(ShaderSource[] sources)
|
||||||
{
|
{
|
||||||
using MemoryStream output = new MemoryStream();
|
using MemoryStream output = MemoryStreamManager.Shared.GetStream();
|
||||||
using BinaryWriter writer = new BinaryWriter(output);
|
|
||||||
|
|
||||||
writer.Write(sources.Length);
|
output.Write(sources.Length);
|
||||||
|
|
||||||
for (int i = 0; i < sources.Length; i++)
|
foreach (ShaderSource source in sources)
|
||||||
{
|
{
|
||||||
writer.Write((int)sources[i].Stage);
|
output.Write((int)source.Stage);
|
||||||
writer.Write(sources[i].BinaryCode.Length);
|
output.Write(source.BinaryCode.Length);
|
||||||
writer.Write(sources[i].BinaryCode);
|
output.Write(source.BinaryCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
|
@@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||||||
{
|
{
|
||||||
result = Marshal.ReadInt64(_bufferMap);
|
result = Marshal.ReadInt64(_bufferMap);
|
||||||
|
|
||||||
return WaitingForValue(result);
|
return !WaitingForValue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long AwaitResult(AutoResetEvent wakeSignal = null)
|
public long AwaitResult(AutoResetEvent wakeSignal = null)
|
||||||
|
@@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem;
|
|||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Ncm;
|
using LibHac.Tools.Ncm;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Services.Ssl;
|
using Ryujinx.HLE.HOS.Services.Ssl;
|
||||||
@@ -637,12 +638,12 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
private Stream GetZipStream(ZipArchiveEntry entry)
|
private Stream GetZipStream(ZipArchiveEntry entry)
|
||||||
{
|
{
|
||||||
MemoryStream dest = new MemoryStream();
|
MemoryStream dest = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
Stream src = entry.Open();
|
using (Stream src = entry.Open())
|
||||||
|
{
|
||||||
src.CopyTo(dest);
|
src.CopyTo(dest);
|
||||||
src.Dispose();
|
}
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
|
|||||||
|
|
||||||
private byte[] BuildResponseOld(WebCommonReturnValue result)
|
private byte[] BuildResponseOld(WebCommonReturnValue result)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(result);
|
writer.WriteStruct(result);
|
||||||
@@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
|
|||||||
}
|
}
|
||||||
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
|
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(new WebArgHeader
|
writer.WriteStruct(new WebArgHeader
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||||
@@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
private byte[] BuildResponse(ControllerSupportResultInfo result)
|
private byte[] BuildResponse(ControllerSupportResultInfo result)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
|
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
|
||||||
@@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
private byte[] BuildResponse()
|
private byte[] BuildResponse()
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write((ulong)ResultCode.Success);
|
writer.Write((ulong)ResultCode.Success);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
{
|
{
|
||||||
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
|
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.Write((ulong)PlayerSelectResult.Success);
|
writer.Write((ulong)PlayerSelectResult.Success);
|
||||||
|
@@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
||||||
|
|
||||||
int[] defaultCapabilities = new int[]
|
uint[] defaultCapabilities = new uint[]
|
||||||
{
|
{
|
||||||
0x030363F7,
|
0x030363F7,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
@@ -552,4 +552,4 @@ namespace Ryujinx.HLE.HOS
|
|||||||
IsPaused = pause;
|
IsPaused = pause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,3 +1,6 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
|
|
||||||
HasPId = (word & 1) != 0;
|
HasPId = (word & 1) != 0;
|
||||||
|
|
||||||
ToCopy = new int[(word >> 1) & 0xf];
|
|
||||||
ToMove = new int[(word >> 5) & 0xf];
|
|
||||||
|
|
||||||
PId = HasPId ? reader.ReadUInt64() : 0;
|
PId = HasPId ? reader.ReadUInt64() : 0;
|
||||||
|
|
||||||
for (int index = 0; index < ToCopy.Length; index++)
|
int toCopySize = (word >> 1) & 0xf;
|
||||||
|
int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
|
||||||
|
|
||||||
|
for (int index = 0; index < toCopy.Length; index++)
|
||||||
{
|
{
|
||||||
ToCopy[index] = reader.ReadInt32();
|
toCopy[index] = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < ToMove.Length; index++)
|
ToCopy = toCopy;
|
||||||
|
|
||||||
|
int toMoveSize = (word >> 5) & 0xf;
|
||||||
|
int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
|
||||||
|
|
||||||
|
for (int index = 0; index < toMove.Length; index++)
|
||||||
{
|
{
|
||||||
ToMove[index] = reader.ReadInt32();
|
toMove[index] = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToMove = toMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move)
|
public IpcHandleDesc(int[] copy, int[] move)
|
||||||
@@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
return new IpcHandleDesc(Array.Empty<int>(), handles);
|
return new IpcHandleDesc(Array.Empty<int>(), handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes()
|
public RecyclableMemoryStream GetStream()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word = HasPId ? 1 : 0;
|
||||||
|
|
||||||
|
word |= (ToCopy.Length & 0xf) << 1;
|
||||||
|
word |= (ToMove.Length & 0xf) << 5;
|
||||||
|
|
||||||
|
ms.Write(word);
|
||||||
|
|
||||||
|
if (HasPId)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
ms.Write(PId);
|
||||||
|
|
||||||
int word = HasPId ? 1 : 0;
|
|
||||||
|
|
||||||
word |= (ToCopy.Length & 0xf) << 1;
|
|
||||||
word |= (ToMove.Length & 0xf) << 5;
|
|
||||||
|
|
||||||
writer.Write(word);
|
|
||||||
|
|
||||||
if (HasPId)
|
|
||||||
{
|
|
||||||
writer.Write(PId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToCopy)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToMove)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(ToCopy);
|
||||||
|
ms.Write(ToMove);
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,8 @@
|
|||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
ObjectIds = new List<int>();
|
ObjectIds = new List<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcMessage(byte[] data, long cmdPtr) : this()
|
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream(data))
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
|
||||||
{
|
{
|
||||||
BinaryReader reader = new BinaryReader(ms);
|
BinaryReader reader = new BinaryReader(ms);
|
||||||
|
|
||||||
@@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
|
|
||||||
for (int index = 0; index < recvListCount; index++)
|
for (int index = 0; index < recvListCount; index++)
|
||||||
{
|
{
|
||||||
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
|
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
|
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word0;
|
||||||
|
int word1;
|
||||||
|
|
||||||
|
word0 = (int)Type;
|
||||||
|
word0 |= (PtrBuff.Count & 0xf) << 16;
|
||||||
|
word0 |= (SendBuff.Count & 0xf) << 20;
|
||||||
|
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||||
|
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||||
|
|
||||||
|
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
|
||||||
|
|
||||||
|
int dataLength = RawData?.Length ?? 0;
|
||||||
|
|
||||||
|
dataLength = (dataLength + 3) & ~3;
|
||||||
|
|
||||||
|
int rawLength = dataLength;
|
||||||
|
|
||||||
|
int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
|
||||||
|
|
||||||
|
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
||||||
|
// padding before Raw Data too, we need to subtract the size of this padding.
|
||||||
|
// This is the weirdest padding I've seen so far...
|
||||||
|
int pad1 = 0x10 - pad0;
|
||||||
|
|
||||||
|
dataLength = (dataLength + pad0 + pad1) / 4;
|
||||||
|
|
||||||
|
word1 = (dataLength & 0x3ff) | (2 << 10);
|
||||||
|
|
||||||
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
word1 |= 1 << 31;
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (PtrBuff.Count & 0xf) << 16;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = Array.Empty<byte>();
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + 3) & ~3;
|
|
||||||
|
|
||||||
int rawLength = dataLength;
|
|
||||||
|
|
||||||
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
|
|
||||||
|
|
||||||
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
|
||||||
// padding before Raw Data too, we need to subtract the size of this padding.
|
|
||||||
// This is the weirdest padding I've seen so far...
|
|
||||||
int pad1 = 0x10 - pad0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + pad0 + pad1) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff) | (2 << 10);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
for (int index = 0; index < PtrBuff.Count; index++)
|
|
||||||
{
|
|
||||||
writer.Write(PtrBuff[index].GetWord0());
|
|
||||||
writer.Write(PtrBuff[index].GetWord1());
|
|
||||||
}
|
|
||||||
|
|
||||||
ms.Seek(pad0, SeekOrigin.Current);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(new byte[pad1]);
|
|
||||||
writer.Write(recvListAddr);
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(word0);
|
||||||
|
ms.Write(word1);
|
||||||
|
|
||||||
|
if (handleDataStream != null)
|
||||||
|
{
|
||||||
|
ms.Write(handleDataStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
|
||||||
|
{
|
||||||
|
ms.Write(ptrBuffDesc.GetWord0());
|
||||||
|
ms.Write(ptrBuffDesc.GetWord1());
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.WriteByte(0, pad0);
|
||||||
|
|
||||||
|
if (RawData != null)
|
||||||
|
{
|
||||||
|
ms.Write(RawData);
|
||||||
|
ms.WriteByte(0, rawLength - RawData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.WriteByte(0, pad1);
|
||||||
|
|
||||||
|
ms.Write(recvListAddr);
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytesTipc()
|
public RecyclableMemoryStream GetStreamTipc()
|
||||||
{
|
{
|
||||||
Debug.Assert(PtrBuff.Count == 0);
|
Debug.Assert(PtrBuff.Count == 0);
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
|
||||||
|
|
||||||
|
int word0;
|
||||||
|
int word1;
|
||||||
|
|
||||||
|
word0 = (int)Type;
|
||||||
|
word0 |= (SendBuff.Count & 0xf) << 20;
|
||||||
|
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||||
|
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||||
|
|
||||||
|
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
|
||||||
|
|
||||||
|
int dataLength = RawData?.Length ?? 0;
|
||||||
|
|
||||||
|
dataLength = ((dataLength + 3) & ~3) / 4;
|
||||||
|
|
||||||
|
word1 = (dataLength & 0x3ff);
|
||||||
|
|
||||||
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
word1 |= 1 << 31;
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = Array.Empty<byte>();
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = ((dataLength + 3) & ~3) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms.Write(word0);
|
||||||
|
ms.Write(word1);
|
||||||
|
|
||||||
|
if (handleDataStream != null)
|
||||||
|
{
|
||||||
|
ms.Write(handleDataStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RawData != null)
|
||||||
|
{
|
||||||
|
ms.Write(RawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetPadSize16(long position)
|
private long GetPadSize16(long position)
|
||||||
|
@@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
Size = size;
|
Size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcRecvListBuffDesc(BinaryReader reader)
|
public IpcRecvListBuffDesc(ulong packedValue)
|
||||||
{
|
{
|
||||||
ulong value = reader.ReadUInt64();
|
Position = packedValue & 0xffffffffffff;
|
||||||
|
|
||||||
Position = value & 0xffffffffffff;
|
Size = (ushort)(packedValue >> 48);
|
||||||
|
|
||||||
Size = (ushort)(value >> 48);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
|
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
|
||||||
{
|
{
|
||||||
Object = schedulerObj;
|
Object = schedulerObj;
|
||||||
TimePoint = timePoint;
|
TimePoint = timePoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
private bool _keepRunning;
|
private bool _keepRunning;
|
||||||
private long _enforceWakeupFromSpinWait;
|
private long _enforceWakeupFromSpinWait;
|
||||||
|
|
||||||
|
private const long NanosecondsPerSecond = 1000000000L;
|
||||||
|
private const long NanosecondsPerMillisecond = 1000000L;
|
||||||
|
|
||||||
public KTimeManager(KernelContext context)
|
public KTimeManager(KernelContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
@@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
{
|
{
|
||||||
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
|
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
|
||||||
|
|
||||||
if (timeout < 1000000)
|
if (timeout < NanosecondsPerMillisecond)
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
|
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
private WaitingObject GetNextWaitingObject()
|
private WaitingObject GetNextWaitingObject()
|
||||||
{
|
{
|
||||||
WaitingObject selected = null;
|
WaitingObject selected = null;
|
||||||
|
|
||||||
long lowestTimePoint = long.MaxValue;
|
long lowestTimePoint = long.MaxValue;
|
||||||
|
|
||||||
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
|
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
|
||||||
@@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public static long ConvertNanosecondsToMilliseconds(long time)
|
public static long ConvertNanosecondsToMilliseconds(long time)
|
||||||
{
|
{
|
||||||
time /= 1000000;
|
time /= NanosecondsPerMillisecond;
|
||||||
|
|
||||||
if ((ulong)time > int.MaxValue)
|
if ((ulong)time > int.MaxValue)
|
||||||
{
|
{
|
||||||
@@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
|
|
||||||
public static long ConvertMillisecondsToNanoseconds(long time)
|
public static long ConvertMillisecondsToNanoseconds(long time)
|
||||||
{
|
{
|
||||||
return time * 1000000;
|
return time * NanosecondsPerMillisecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertNanosecondsToHostTicks(long ns)
|
public static long ConvertNanosecondsToHostTicks(long ns)
|
||||||
{
|
{
|
||||||
long nsDiv = ns / 1000000000;
|
long nsDiv = ns / NanosecondsPerSecond;
|
||||||
long nsMod = ns % 1000000000;
|
long nsMod = ns % NanosecondsPerSecond;
|
||||||
long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
|
long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
|
||||||
long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
|
long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
|
||||||
|
|
||||||
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
|
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
|
||||||
return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
|
return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertGuestTicksToNanoseconds(long ticks)
|
public static long ConvertGuestTicksToNanoseconds(long ticks)
|
||||||
|
@@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
public const int InitialKipId = 1;
|
public const int InitialKipId = 1;
|
||||||
public const int InitialProcessId = 0x51;
|
public const int InitialProcessId = 0x51;
|
||||||
|
|
||||||
|
public const int SupervisorCallCount = 0xC0;
|
||||||
|
|
||||||
public const int MemoryBlockAllocatorSize = 0x2710;
|
public const int MemoryBlockAllocatorSize = 0x2710;
|
||||||
|
|
||||||
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||||
@@ -15,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
|
|
||||||
public const ulong CounterFrequency = 19200000;
|
public const ulong CounterFrequency = 19200000;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
public static Result StartInitialProcess(
|
public static Result StartInitialProcess(
|
||||||
KernelContext context,
|
KernelContext context,
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
int mainThreadPriority,
|
int mainThreadPriority,
|
||||||
ThreadStart customThreadStart)
|
ThreadStart customThreadStart)
|
||||||
{
|
{
|
||||||
|
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
{
|
||||||
|
static class CapabilityExtensions
|
||||||
|
{
|
||||||
|
public static CapabilityType GetCapabilityType(this uint cap)
|
||||||
|
{
|
||||||
|
return (CapabilityType)(((cap + 1) & ~cap) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint GetFlag(this CapabilityType type)
|
||||||
|
{
|
||||||
|
return (uint)type + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint GetId(this CapabilityType type)
|
||||||
|
{
|
||||||
|
return (uint)BitOperations.TrailingZeroCount(type.GetFlag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
{
|
||||||
|
enum CapabilityType : uint
|
||||||
|
{
|
||||||
|
CorePriority = (1u << 3) - 1,
|
||||||
|
SyscallMask = (1u << 4) - 1,
|
||||||
|
MapRange = (1u << 6) - 1,
|
||||||
|
MapIoPage = (1u << 7) - 1,
|
||||||
|
MapRegion = (1u << 10) - 1,
|
||||||
|
InterruptPair = (1u << 11) - 1,
|
||||||
|
ProgramType = (1u << 13) - 1,
|
||||||
|
KernelVersion = (1u << 14) - 1,
|
||||||
|
HandleTable = (1u << 15) - 1,
|
||||||
|
DebugFlags = (1u << 16) - 1,
|
||||||
|
|
||||||
|
Invalid = 0u,
|
||||||
|
Padding = ~0u
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
private int _activeSlotsCount;
|
private int _activeSlotsCount;
|
||||||
|
|
||||||
private int _size;
|
private uint _size;
|
||||||
|
|
||||||
private ushort _idCounter;
|
private ushort _idCounter;
|
||||||
|
|
||||||
@@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(int size)
|
public Result Initialize(uint size)
|
||||||
{
|
{
|
||||||
if ((uint)size > 1024)
|
if (size > 1024)
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
@@ -16,11 +16,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class KProcess : KSynchronizationObject
|
class KProcess : KSynchronizationObject
|
||||||
{
|
{
|
||||||
public const int KernelVersionMajor = 10;
|
public const uint KernelVersionMajor = 10;
|
||||||
public const int KernelVersionMinor = 4;
|
public const uint KernelVersionMinor = 4;
|
||||||
public const int KernelVersionRevision = 0;
|
public const uint KernelVersionRevision = 0;
|
||||||
|
|
||||||
public const int KernelVersionPacked =
|
public const uint KernelVersionPacked =
|
||||||
(KernelVersionMajor << 19) |
|
(KernelVersionMajor << 19) |
|
||||||
(KernelVersionMinor << 15) |
|
(KernelVersionMinor << 15) |
|
||||||
(KernelVersionRevision << 0);
|
(KernelVersionRevision << 0);
|
||||||
@@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public Result InitializeKip(
|
public Result InitializeKip(
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
KPageList pageList,
|
KPageList pageList,
|
||||||
KResourceLimit resourceLimit,
|
KResourceLimit resourceLimit,
|
||||||
MemoryRegion memRegion,
|
MemoryRegion memRegion,
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
public Result Initialize(
|
public Result Initialize(
|
||||||
ProcessCreationInfo creationInfo,
|
ProcessCreationInfo creationInfo,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
KResourceLimit resourceLimit,
|
KResourceLimit resourceLimit,
|
||||||
MemoryRegion memRegion,
|
MemoryRegion memRegion,
|
||||||
IProcessContextFactory contextFactory,
|
IProcessContextFactory contextFactory,
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
@@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class KProcessCapabilities
|
class KProcessCapabilities
|
||||||
{
|
{
|
||||||
public byte[] SvcAccessMask { get; private set; }
|
public byte[] SvcAccessMask { get; }
|
||||||
public byte[] IrqAccessMask { get; private set; }
|
public byte[] IrqAccessMask { get; }
|
||||||
|
|
||||||
public ulong AllowedCpuCoresMask { get; private set; }
|
public ulong AllowedCpuCoresMask { get; private set; }
|
||||||
public ulong AllowedThreadPriosMask { get; private set; }
|
public ulong AllowedThreadPriosMask { get; private set; }
|
||||||
|
|
||||||
public int DebuggingFlags { get; private set; }
|
public uint DebuggingFlags { get; private set; }
|
||||||
public int HandleTableSize { get; private set; }
|
public uint HandleTableSize { get; private set; }
|
||||||
public int KernelReleaseVersion { get; private set; }
|
public uint KernelReleaseVersion { get; private set; }
|
||||||
public int ApplicationType { get; private set; }
|
public uint ApplicationType { get; private set; }
|
||||||
|
|
||||||
public KProcessCapabilities()
|
public KProcessCapabilities()
|
||||||
{
|
{
|
||||||
SvcAccessMask = new byte[0x10];
|
// length / number of bits of the underlying type
|
||||||
|
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
|
||||||
IrqAccessMask = new byte[0x80];
|
IrqAccessMask = new byte[0x80];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result InitializeForKernel(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
AllowedCpuCoresMask = 0xf;
|
AllowedCpuCoresMask = 0xf;
|
||||||
AllowedThreadPriosMask = ulong.MaxValue;
|
AllowedThreadPriosMask = ulong.MaxValue;
|
||||||
DebuggingFlags &= ~3;
|
DebuggingFlags &= ~3u;
|
||||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||||
|
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result Parse(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
int mask0 = 0;
|
int mask0 = 0;
|
||||||
int mask1 = 0;
|
int mask1 = 0;
|
||||||
|
|
||||||
for (int index = 0; index < capabilities.Length; index++)
|
for (int index = 0; index < capabilities.Length; index++)
|
||||||
{
|
{
|
||||||
int cap = capabilities[index];
|
uint cap = capabilities[index];
|
||||||
|
|
||||||
if (((cap + 1) & ~cap) != 0x40)
|
if (cap.GetCapabilityType() != CapabilityType.MapRange)
|
||||||
{
|
{
|
||||||
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
int prevCap = cap;
|
uint prevCap = cap;
|
||||||
|
|
||||||
cap = capabilities[++index];
|
cap = capabilities[++index];
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
long address = ((long)(uint)prevCap << 5) & 0xffffff000;
|
long address = ((long)prevCap << 5) & 0xffffff000;
|
||||||
long size = ((long)(uint)cap << 5) & 0xfffff000;
|
long size = ((long)cap << 5) & 0xfffff000;
|
||||||
|
|
||||||
if (((ulong)(address + size - 1) >> 36) != 0)
|
if (((ulong)(address + size - 1) >> 36) != 0)
|
||||||
{
|
{
|
||||||
@@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
|
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
|
||||||
{
|
{
|
||||||
int code = (cap + 1) & ~cap;
|
CapabilityType code = cap.GetCapabilityType();
|
||||||
|
|
||||||
if (code == 1)
|
if (code == CapabilityType.Invalid)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCapability;
|
return KernelResult.InvalidCapability;
|
||||||
}
|
}
|
||||||
else if (code == 0)
|
else if (code == CapabilityType.Padding)
|
||||||
{
|
{
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1));
|
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1));
|
||||||
|
|
||||||
// Check if the property was already set.
|
// Check if the property was already set.
|
||||||
if (((mask0 & codeMask) & 0x1e008) != 0)
|
if (((mask0 & codeMask) & 0x1e008) != 0)
|
||||||
@@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 8:
|
case CapabilityType.CorePriority:
|
||||||
{
|
{
|
||||||
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCapability;
|
return KernelResult.InvalidCapability;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lowestCpuCore = (cap >> 16) & 0xff;
|
uint lowestCpuCore = (cap >> 16) & 0xff;
|
||||||
int highestCpuCore = (cap >> 24) & 0xff;
|
uint highestCpuCore = (cap >> 24) & 0xff;
|
||||||
|
|
||||||
if (lowestCpuCore > highestCpuCore)
|
if (lowestCpuCore > highestCpuCore)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
int highestThreadPrio = (cap >> 4) & 0x3f;
|
uint highestThreadPrio = (cap >> 4) & 0x3f;
|
||||||
int lowestThreadPrio = (cap >> 10) & 0x3f;
|
uint lowestThreadPrio = (cap >> 10) & 0x3f;
|
||||||
|
|
||||||
if (lowestThreadPrio > highestThreadPrio)
|
if (lowestThreadPrio > highestThreadPrio)
|
||||||
{
|
{
|
||||||
@@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x10:
|
case CapabilityType.SyscallMask:
|
||||||
{
|
{
|
||||||
int slot = (cap >> 29) & 7;
|
int slot = ((int)cap >> 29) & 7;
|
||||||
|
|
||||||
int svcSlotMask = 1 << slot;
|
int svcSlotMask = 1 << slot;
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
mask1 |= svcSlotMask;
|
mask1 |= svcSlotMask;
|
||||||
|
|
||||||
int svcMask = (cap >> 5) & 0xffffff;
|
uint svcMask = (cap >> 5) & 0xffffff;
|
||||||
|
|
||||||
int baseSvc = slot * 24;
|
int baseSvc = slot * 24;
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
int svcId = baseSvc + index;
|
int svcId = baseSvc + index;
|
||||||
|
|
||||||
if (svcId > 0x7f)
|
if (svcId >= KernelConstants.SupervisorCallCount)
|
||||||
{
|
{
|
||||||
return KernelResult.MaximumExceeded;
|
return KernelResult.MaximumExceeded;
|
||||||
}
|
}
|
||||||
@@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x80:
|
case CapabilityType.MapIoPage:
|
||||||
{
|
{
|
||||||
long address = ((long)(uint)cap << 4) & 0xffffff000;
|
long address = ((long)cap << 4) & 0xffffff000;
|
||||||
|
|
||||||
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
|
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x800:
|
case CapabilityType.MapRegion:
|
||||||
|
{
|
||||||
|
// TODO: Implement capabilities for MapRegion
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CapabilityType.InterruptPair:
|
||||||
{
|
{
|
||||||
// TODO: GIC distributor check.
|
// TODO: GIC distributor check.
|
||||||
int irq0 = (cap >> 12) & 0x3ff;
|
int irq0 = ((int)cap >> 12) & 0x3ff;
|
||||||
int irq1 = (cap >> 22) & 0x3ff;
|
int irq1 = ((int)cap >> 22) & 0x3ff;
|
||||||
|
|
||||||
if (irq0 != 0x3ff)
|
if (irq0 != 0x3ff)
|
||||||
{
|
{
|
||||||
@@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2000:
|
case CapabilityType.ProgramType:
|
||||||
{
|
{
|
||||||
int applicationType = cap >> 14;
|
uint applicationType = (cap >> 14);
|
||||||
|
|
||||||
if ((uint)applicationType > 7)
|
if (applicationType > 7)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
@@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x4000:
|
case CapabilityType.KernelVersion:
|
||||||
{
|
{
|
||||||
// Note: This check is bugged on kernel too, we are just replicating the bug here.
|
// Note: This check is bugged on kernel too, we are just replicating the bug here.
|
||||||
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
||||||
@@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x8000:
|
case CapabilityType.HandleTable:
|
||||||
{
|
{
|
||||||
int handleTableSize = cap >> 26;
|
uint handleTableSize = cap >> 26;
|
||||||
|
|
||||||
if ((uint)handleTableSize > 0x3ff)
|
if (handleTableSize > 0x3ff)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
@@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x10000:
|
case CapabilityType.DebugFlags:
|
||||||
{
|
{
|
||||||
int debuggingFlags = cap >> 19;
|
uint debuggingFlags = cap >> 19;
|
||||||
|
|
||||||
if ((uint)debuggingFlags > 3)
|
if (debuggingFlags > 3)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebuggingFlags &= ~3;
|
DebuggingFlags &= ~3u;
|
||||||
DebuggingFlags |= debuggingFlags;
|
DebuggingFlags |= debuggingFlags;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong GetMaskFromMinMax(int min, int max)
|
private static ulong GetMaskFromMinMax(uint min, uint max)
|
||||||
{
|
{
|
||||||
int range = max - min + 1;
|
uint range = max - min + 1;
|
||||||
|
|
||||||
if (range == 64)
|
if (range == 64)
|
||||||
{
|
{
|
||||||
return ulong.MaxValue;
|
return ulong.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong mask = (1UL << range) - 1;
|
ulong mask = (1UL << (int)range) - 1;
|
||||||
|
|
||||||
return mask << min;
|
return mask << (int)min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,8 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
enum ProcessCreationFlags
|
enum ProcessCreationFlags
|
||||||
{
|
{
|
||||||
Is64Bit = 1 << 0,
|
Is64Bit = 1 << 0,
|
||||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
public Result CreateProcess(
|
public Result CreateProcess(
|
||||||
out int handle,
|
out int handle,
|
||||||
ProcessCreationInfo info,
|
ProcessCreationInfo info,
|
||||||
ReadOnlySpan<int> capabilities,
|
ReadOnlySpan<uint> capabilities,
|
||||||
IProcessContextFactory contextFactory,
|
IProcessContextFactory contextFactory,
|
||||||
ThreadStart customThreadStart = null)
|
ThreadStart customThreadStart = null)
|
||||||
{
|
{
|
||||||
@@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
|
KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
|
||||||
|
|
||||||
for (int index = 0; index < handles.Length; index++)
|
for (int index = 0; index < handles.Length; index++)
|
||||||
{
|
{
|
||||||
@@ -3002,4 +3002,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return (address & 3) != 0;
|
return (address & 3) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
class KPriorityQueue
|
class KPriorityQueue
|
||||||
{
|
{
|
||||||
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
|
private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
|
||||||
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
|
private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
|
||||||
|
|
||||||
private long[] _scheduledPrioritiesPerCore;
|
private readonly long[] _scheduledPrioritiesPerCore;
|
||||||
private long[] _suggestedPrioritiesPerCore;
|
private readonly long[] _suggestedPrioritiesPerCore;
|
||||||
|
|
||||||
public KPriorityQueue()
|
public KPriorityQueue()
|
||||||
{
|
{
|
||||||
@@ -32,43 +32,134 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
|
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<KThread> SuggestedThreads(int core)
|
public readonly ref struct KThreadEnumerable
|
||||||
{
|
{
|
||||||
return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
|
readonly LinkedList<KThread>[][] _listPerPrioPerCore;
|
||||||
}
|
readonly long[] _prios;
|
||||||
|
readonly int _core;
|
||||||
|
|
||||||
public IEnumerable<KThread> ScheduledThreads(int core)
|
public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
||||||
{
|
|
||||||
return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
|
||||||
{
|
|
||||||
long prioMask = prios[core];
|
|
||||||
|
|
||||||
int prio = BitOperations.TrailingZeroCount(prioMask);
|
|
||||||
|
|
||||||
prioMask &= ~(1L << prio);
|
|
||||||
|
|
||||||
while (prio < KScheduler.PrioritiesCount)
|
|
||||||
{
|
{
|
||||||
LinkedList<KThread> list = listPerPrioPerCore[prio][core];
|
_listPerPrioPerCore = listPerPrioPerCore;
|
||||||
|
_prios = prios;
|
||||||
|
_core = core;
|
||||||
|
}
|
||||||
|
|
||||||
LinkedListNode<KThread> node = list.First;
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return new Enumerator(_listPerPrioPerCore, _prios, _core);
|
||||||
|
}
|
||||||
|
|
||||||
while (node != null)
|
public ref struct Enumerator
|
||||||
|
{
|
||||||
|
private readonly LinkedList<KThread>[][] _listPerPrioPerCore;
|
||||||
|
private readonly int _core;
|
||||||
|
private long _prioMask;
|
||||||
|
private int _prio;
|
||||||
|
private LinkedList<KThread> _list;
|
||||||
|
private LinkedListNode<KThread> _node;
|
||||||
|
|
||||||
|
public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
|
||||||
{
|
{
|
||||||
yield return node.Value;
|
_listPerPrioPerCore = listPerPrioPerCore;
|
||||||
|
_core = core;
|
||||||
node = node.Next;
|
_prioMask = prios[core];
|
||||||
|
_prio = BitOperations.TrailingZeroCount(_prioMask);
|
||||||
|
_prioMask &= ~(1L << _prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
prio = BitOperations.TrailingZeroCount(prioMask);
|
public KThread Current => _node?.Value;
|
||||||
|
|
||||||
prioMask &= ~(1L << prio);
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
_node = _node?.Next;
|
||||||
|
|
||||||
|
if (_node == null)
|
||||||
|
{
|
||||||
|
if (!MoveNextListAndFirstNode())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _node != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveNextListAndFirstNode()
|
||||||
|
{
|
||||||
|
if (_prio < KScheduler.PrioritiesCount)
|
||||||
|
{
|
||||||
|
_list = _listPerPrioPerCore[_prio][_core];
|
||||||
|
|
||||||
|
_node = _list.First;
|
||||||
|
|
||||||
|
_prio = BitOperations.TrailingZeroCount(_prioMask);
|
||||||
|
|
||||||
|
_prioMask &= ~(1L << _prio);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_list = null;
|
||||||
|
_node = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KThreadEnumerable ScheduledThreads(int core)
|
||||||
|
{
|
||||||
|
return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThreadEnumerable SuggestedThreads(int core)
|
||||||
|
{
|
||||||
|
return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsFirstOrDefault(int core)
|
||||||
|
{
|
||||||
|
return ScheduledThreadsElementAtOrDefault(core, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
|
||||||
|
{
|
||||||
|
int currentIndex = 0;
|
||||||
|
foreach (var scheduledThread in ScheduledThreads(core))
|
||||||
|
{
|
||||||
|
if (currentIndex == index)
|
||||||
|
{
|
||||||
|
return scheduledThread;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
|
||||||
|
{
|
||||||
|
foreach (var scheduledThread in ScheduledThreads(core))
|
||||||
|
{
|
||||||
|
if (scheduledThread.DynamicPriority == dynamicPriority)
|
||||||
|
{
|
||||||
|
return scheduledThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasScheduledThreads(int core)
|
||||||
|
{
|
||||||
|
return ScheduledThreadsFirstOrDefault(core) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public void TransferToCore(int prio, int dstCore, KThread thread)
|
public void TransferToCore(int prio, int dstCore, KThread thread)
|
||||||
{
|
{
|
||||||
int srcCore = thread.ActiveCore;
|
int srcCore = thread.ActiveCore;
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
|
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
|
||||||
|
|
||||||
|
private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private readonly int _coreId;
|
private readonly int _coreId;
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
for (int core = 0; core < CpuCoresCount; core++)
|
for (int core = 0; core < CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
|
KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
|
||||||
|
|
||||||
if (thread != null &&
|
if (thread != null &&
|
||||||
thread.Owner != null &&
|
thread.Owner != null &&
|
||||||
@@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
// If the core is not idle (there's already a thread running on it),
|
// If the core is not idle (there's already a thread running on it),
|
||||||
// then we don't need to attempt load balancing.
|
// then we don't need to attempt load balancing.
|
||||||
if (context.PriorityQueue.ScheduledThreads(core).Any())
|
if (context.PriorityQueue.HasScheduledThreads(core))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
Array.Fill(_srcCoresHighestPrioThreads, 0);
|
||||||
|
|
||||||
int srcCoresHighestPrioThreadsCount = 0;
|
int srcCoresHighestPrioThreadsCount = 0;
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
|
_srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not yet selected candidate found.
|
// Not yet selected candidate found.
|
||||||
@@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// (the first one that doesn't make the source core idle if moved).
|
// (the first one that doesn't make the source core idle if moved).
|
||||||
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
|
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
|
||||||
{
|
{
|
||||||
int srcCore = srcCoresHighestPrioThreads[index];
|
int srcCore = _srcCoresHighestPrioThreads[index];
|
||||||
|
|
||||||
KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
|
KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1);
|
||||||
|
|
||||||
if (src != null)
|
if (src != null)
|
||||||
{
|
{
|
||||||
@@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
|
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
|
||||||
{
|
{
|
||||||
IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
|
KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio);
|
||||||
|
|
||||||
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
|
|
||||||
KThread nextThread = null;
|
KThread nextThread = null;
|
||||||
|
|
||||||
// Yield priority queue.
|
// Yield priority queue.
|
||||||
@@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
|
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<KThread> SuitableCandidates()
|
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate)
|
||||||
{
|
{
|
||||||
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
||||||
{
|
{
|
||||||
int suggestedCore = suggested.ActiveCore;
|
int suggestedCore = suggested.ActiveCore;
|
||||||
if (suggestedCore >= 0)
|
if (suggestedCore >= 0)
|
||||||
{
|
{
|
||||||
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
|
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
|
||||||
|
|
||||||
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
|
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
|
||||||
{
|
{
|
||||||
@@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
nextThread == null ||
|
nextThread == null ||
|
||||||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
|
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
|
||||||
{
|
{
|
||||||
yield return suggested;
|
if (predicate(suggested))
|
||||||
|
{
|
||||||
|
return suggested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select candidate threads that could run on this core.
|
// Select candidate threads that could run on this core.
|
||||||
// Only take into account threads that are not yet selected.
|
// Only take into account threads that are not yet selected.
|
||||||
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
|
KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
// If the priority of the currently selected thread is lower or same as the preemption priority,
|
// If the priority of the currently selected thread is lower or same as the preemption priority,
|
||||||
// then try to migrate a thread with lower priority.
|
// then try to migrate a thread with lower priority.
|
||||||
KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
|
KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
|
||||||
|
|
||||||
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
|
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
|
||||||
{
|
{
|
||||||
dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
|
dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// Move current thread to the end of the queue.
|
// Move current thread to the end of the queue.
|
||||||
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
|
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
|
||||||
|
|
||||||
IEnumerable<KThread> SuitableCandidates()
|
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority)
|
||||||
{
|
{
|
||||||
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
|
||||||
{
|
{
|
||||||
@@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
|
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
|
||||||
suggested.DynamicPriority < nextThread.DynamicPriority)
|
suggested.DynamicPriority < nextThread.DynamicPriority)
|
||||||
{
|
{
|
||||||
yield return suggested;
|
if (suggested.DynamicPriority <= lessThanOrEqualPriority)
|
||||||
|
{
|
||||||
|
return suggested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
|
KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio);
|
||||||
|
|
||||||
if (dst != null)
|
if (dst != null)
|
||||||
{
|
{
|
||||||
@@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
|
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
|
||||||
|
|
||||||
if (!context.PriorityQueue.ScheduledThreads(core).Any())
|
if (!context.PriorityQueue.HasScheduledThreads(core))
|
||||||
{
|
{
|
||||||
KThread selectedThread = null;
|
KThread selectedThread = null;
|
||||||
|
|
||||||
@@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
|
KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
|
||||||
|
|
||||||
if (firstCandidate == suggested)
|
if (firstCandidate == suggested)
|
||||||
{
|
{
|
||||||
|
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length];
|
LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
|
||||||
|
|
||||||
for (int index = 0; index < syncObjs.Length; index++)
|
for (int index = 0; index < syncObjs.Length; index++)
|
||||||
{
|
{
|
||||||
|
@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
|
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
|
||||||
|
|
||||||
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
|
ulong codeAddress = codeBaseAddress + kip.TextOffset;
|
||||||
|
|
||||||
ProcessCreationFlags flags = 0;
|
ProcessCreationFlags flags = 0;
|
||||||
|
|
||||||
@@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
||||||
|
|
||||||
nsoBase[index] = codeStart + (ulong)codeSize;
|
nsoBase[index] = codeStart + codeSize;
|
||||||
|
|
||||||
codeSize += nsoSize;
|
codeSize += nsoSize;
|
||||||
|
|
||||||
if (arguments != null && argsSize == 0)
|
if (arguments != null && argsSize == 0)
|
||||||
{
|
{
|
||||||
argsStart = (ulong)codeSize;
|
argsStart = codeSize;
|
||||||
|
|
||||||
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
|
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
result = process.Initialize(
|
result = process.Initialize(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
MemoryMarshal.Cast<byte, int>(npdm.KernelCapabilityData).ToArray(),
|
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
|
||||||
resourceLimit,
|
resourceLimit,
|
||||||
memoryRegion,
|
memoryRegion,
|
||||||
processContextFactory);
|
processContextFactory);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
||||||
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
|
|||||||
public static byte[] MakeLaunchParams(UserProfile userProfile)
|
public static byte[] MakeLaunchParams(UserProfile userProfile)
|
||||||
{
|
{
|
||||||
// Size needs to be at least 0x88 bytes otherwise application errors.
|
// Size needs to be at least 0x88 bytes otherwise application errors.
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter writer = new BinaryWriter(ms);
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
@@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
|
|||||||
static uint KXor(uint data) => data ^ FontKey;
|
static uint KXor(uint data) => data ^ FontKey;
|
||||||
|
|
||||||
using (BinaryReader reader = new BinaryReader(bfttfStream))
|
using (BinaryReader reader = new BinaryReader(bfttfStream))
|
||||||
using (MemoryStream ttfStream = new MemoryStream())
|
using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter output = new BinaryWriter(ttfStream))
|
using (BinaryWriter output = new BinaryWriter(ttfStream))
|
||||||
{
|
{
|
||||||
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
|
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
@@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -19,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
// not large enough.
|
// not large enough.
|
||||||
private const int PointerBufferSize = 0x8000;
|
private const int PointerBufferSize = 0x8000;
|
||||||
|
|
||||||
private readonly static int[] DefaultCapabilities = new int[]
|
private readonly static uint[] DefaultCapabilities = new uint[]
|
||||||
{
|
{
|
||||||
0x030363F7,
|
0x030363F7,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
@@ -29,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
0x01007FFF
|
0x01007FFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly object _handleLock = new();
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private KProcess _selfProcess;
|
private KProcess _selfProcess;
|
||||||
|
|
||||||
@@ -37,14 +42,27 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
||||||
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
||||||
|
|
||||||
|
private readonly MemoryStream _requestDataStream;
|
||||||
|
private readonly BinaryReader _requestDataReader;
|
||||||
|
|
||||||
|
private readonly MemoryStream _responseDataStream;
|
||||||
|
private readonly BinaryWriter _responseDataWriter;
|
||||||
|
|
||||||
public ManualResetEvent InitDone { get; }
|
public ManualResetEvent InitDone { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public Func<IpcService> SmObjectFactory { get; }
|
public Func<IpcService> SmObjectFactory { get; }
|
||||||
|
|
||||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
||||||
{
|
{
|
||||||
InitDone = new ManualResetEvent(false);
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
|
_requestDataStream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
_requestDataReader = new BinaryReader(_requestDataStream);
|
||||||
|
|
||||||
|
_responseDataStream = MemoryStreamManager.Shared.GetStream();
|
||||||
|
_responseDataWriter = new BinaryWriter(_responseDataStream);
|
||||||
|
|
||||||
|
InitDone = new ManualResetEvent(false);
|
||||||
Name = name;
|
Name = name;
|
||||||
SmObjectFactory = smObjectFactory;
|
SmObjectFactory = smObjectFactory;
|
||||||
|
|
||||||
@@ -61,7 +79,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
||||||
{
|
{
|
||||||
_portHandles.Add(serverPortHandle);
|
lock (_handleLock)
|
||||||
|
{
|
||||||
|
_portHandles.Add(serverPortHandle);
|
||||||
|
}
|
||||||
_ports.Add(serverPortHandle, objectFactory);
|
_ports.Add(serverPortHandle, objectFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +97,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
||||||
{
|
{
|
||||||
_sessionHandles.Add(serverSessionHandle);
|
lock (_handleLock)
|
||||||
|
{
|
||||||
|
_sessionHandles.Add(serverSessionHandle);
|
||||||
|
}
|
||||||
_sessions.Add(serverSessionHandle, obj);
|
_sessions.Add(serverSessionHandle, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,15 +134,23 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int[] portHandles = _portHandles.ToArray();
|
int handleCount;
|
||||||
int[] sessionHandles = _sessionHandles.ToArray();
|
int portHandleCount;
|
||||||
int[] handles = new int[portHandles.Length + sessionHandles.Length];
|
int[] handles;
|
||||||
|
|
||||||
portHandles.CopyTo(handles, 0);
|
lock (_handleLock)
|
||||||
sessionHandles.CopyTo(handles, portHandles.Length);
|
{
|
||||||
|
portHandleCount = _portHandles.Count;
|
||||||
|
handleCount = portHandleCount + _sessionHandles.Count;
|
||||||
|
|
||||||
|
handles = ArrayPool<int>.Shared.Rent(handleCount);
|
||||||
|
|
||||||
|
_portHandles.CopyTo(handles, 0);
|
||||||
|
_sessionHandles.CopyTo(handles, portHandleCount);
|
||||||
|
}
|
||||||
|
|
||||||
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
||||||
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
|
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
|
||||||
|
|
||||||
thread.HandlePostSyscall();
|
thread.HandlePostSyscall();
|
||||||
|
|
||||||
@@ -129,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
replyTargetHandle = 0;
|
replyTargetHandle = 0;
|
||||||
|
|
||||||
if (rc == Result.Success && signaledIndex >= portHandles.Length)
|
if (rc == Result.Success && signaledIndex >= portHandleCount)
|
||||||
{
|
{
|
||||||
// We got a IPC request, process it, pass to the appropriate service if needed.
|
// We got a IPC request, process it, pass to the appropriate service if needed.
|
||||||
int signaledHandle = handles[signaledIndex];
|
int signaledHandle = handles[signaledIndex];
|
||||||
@@ -156,6 +188,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<int>.Shared.Return(handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
@@ -166,13 +200,9 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
KThread thread = KernelStatic.GetCurrentThread();
|
||||||
ulong messagePtr = thread.TlsAddress;
|
ulong messagePtr = thread.TlsAddress;
|
||||||
ulong messageSize = 0x100;
|
|
||||||
|
|
||||||
byte[] reqData = new byte[messageSize];
|
IpcMessage request = ReadRequest(process, messagePtr);
|
||||||
|
|
||||||
process.CpuMemory.Read(messagePtr, reqData);
|
|
||||||
|
|
||||||
IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
|
|
||||||
IpcMessage response = new IpcMessage();
|
IpcMessage response = new IpcMessage();
|
||||||
|
|
||||||
ulong tempAddr = recvListAddr;
|
ulong tempAddr = recvListAddr;
|
||||||
@@ -202,158 +232,160 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
bool shouldReply = true;
|
bool shouldReply = true;
|
||||||
bool isTipcCommunication = false;
|
bool isTipcCommunication = false;
|
||||||
|
|
||||||
using (MemoryStream raw = new MemoryStream(request.RawData))
|
_requestDataStream.SetLength(0);
|
||||||
|
_requestDataStream.Write(request.RawData);
|
||||||
|
_requestDataStream.Position = 0;
|
||||||
|
|
||||||
|
if (request.Type == IpcMessageType.HipcRequest ||
|
||||||
|
request.Type == IpcMessageType.HipcRequestWithContext)
|
||||||
{
|
{
|
||||||
BinaryReader reqReader = new BinaryReader(raw);
|
response.Type = IpcMessageType.HipcResponse;
|
||||||
|
|
||||||
if (request.Type == IpcMessageType.HipcRequest ||
|
_responseDataStream.SetLength(0);
|
||||||
request.Type == IpcMessageType.HipcRequestWithContext)
|
|
||||||
|
ServiceCtx context = new ServiceCtx(
|
||||||
|
_context.Device,
|
||||||
|
process,
|
||||||
|
process.CpuMemory,
|
||||||
|
thread,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
_requestDataReader,
|
||||||
|
_responseDataWriter);
|
||||||
|
|
||||||
|
_sessions[serverSessionHandle].CallHipcMethod(context);
|
||||||
|
|
||||||
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
}
|
||||||
|
else if (request.Type == IpcMessageType.HipcControl ||
|
||||||
|
request.Type == IpcMessageType.HipcControlWithContext)
|
||||||
|
{
|
||||||
|
uint magic = (uint)_requestDataReader.ReadUInt64();
|
||||||
|
uint cmdId = (uint)_requestDataReader.ReadUInt64();
|
||||||
|
|
||||||
|
switch (cmdId)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
case 0:
|
||||||
|
FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
||||||
|
break;
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
case 3:
|
||||||
{
|
FillHipcResponse(response, 0, PointerBufferSize);
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
break;
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||||
_context.Device,
|
case 2:
|
||||||
process,
|
case 4:
|
||||||
process.CpuMemory,
|
int unknown = _requestDataReader.ReadInt32();
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallHipcMethod(context);
|
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
||||||
}
|
|
||||||
|
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
||||||
|
|
||||||
|
FillHipcResponse(response, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: throw new NotImplementedException(cmdId.ToString());
|
||||||
}
|
}
|
||||||
else if (request.Type == IpcMessageType.HipcControl ||
|
}
|
||||||
request.Type == IpcMessageType.HipcControlWithContext)
|
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
||||||
|
{
|
||||||
|
_context.Syscall.CloseHandle(serverSessionHandle);
|
||||||
|
lock (_handleLock)
|
||||||
{
|
{
|
||||||
uint magic = (uint)reqReader.ReadUInt64();
|
|
||||||
uint cmdId = (uint)reqReader.ReadUInt64();
|
|
||||||
|
|
||||||
switch (cmdId)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
request = FillResponse(response, 0, PointerBufferSize);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
int unknown = reqReader.ReadInt32();
|
|
||||||
|
|
||||||
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
|
||||||
|
|
||||||
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
|
||||||
|
|
||||||
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
|
||||||
|
|
||||||
request = FillResponse(response, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: throw new NotImplementedException(cmdId.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
|
||||||
{
|
|
||||||
_context.Syscall.CloseHandle(serverSessionHandle);
|
|
||||||
_sessionHandles.Remove(serverSessionHandle);
|
_sessionHandles.Remove(serverSessionHandle);
|
||||||
IpcService service = _sessions[serverSessionHandle];
|
|
||||||
if (service is IDisposable disposableObj)
|
|
||||||
{
|
|
||||||
disposableObj.Dispose();
|
|
||||||
}
|
|
||||||
_sessions.Remove(serverSessionHandle);
|
|
||||||
shouldReply = false;
|
|
||||||
}
|
}
|
||||||
// If the type is past 0xF, we are using TIPC
|
IpcService service = _sessions[serverSessionHandle];
|
||||||
else if (request.Type > IpcMessageType.TipcCloseSession)
|
(service as IDisposable)?.Dispose();
|
||||||
{
|
_sessions.Remove(serverSessionHandle);
|
||||||
isTipcCommunication = true;
|
shouldReply = false;
|
||||||
|
|
||||||
// Response type is always the same as request on TIPC.
|
|
||||||
response.Type = request.Type;
|
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
|
||||||
_context.Device,
|
|
||||||
process,
|
|
||||||
process.CpuMemory,
|
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallTipcMethod(context);
|
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotImplementedException(request.Type.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTipcCommunication)
|
|
||||||
{
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldReply;
|
|
||||||
}
|
}
|
||||||
}
|
// If the type is past 0xF, we are using TIPC
|
||||||
|
else if (request.Type > IpcMessageType.TipcCloseSession)
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
isTipcCommunication = true;
|
||||||
|
|
||||||
foreach (int value in values)
|
// Response type is always the same as request on TIPC.
|
||||||
{
|
response.Type = request.Type;
|
||||||
writer.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FillResponse(response, result, ms.ToArray());
|
_responseDataStream.SetLength(0);
|
||||||
|
|
||||||
|
ServiceCtx context = new ServiceCtx(
|
||||||
|
_context.Device,
|
||||||
|
process,
|
||||||
|
process.CpuMemory,
|
||||||
|
thread,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
_requestDataReader,
|
||||||
|
_responseDataWriter);
|
||||||
|
|
||||||
|
_sessions[serverSessionHandle].CallTipcMethod(context);
|
||||||
|
|
||||||
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
|
||||||
|
using var responseStream = response.GetStreamTipc();
|
||||||
|
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException(request.Type.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTipcCommunication)
|
||||||
|
{
|
||||||
|
using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
|
||||||
|
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
|
private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
|
||||||
|
{
|
||||||
|
const int messageSize = 0x100;
|
||||||
|
|
||||||
|
byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
|
||||||
|
|
||||||
|
Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
|
||||||
|
reqDataSpan.Clear();
|
||||||
|
|
||||||
|
process.CpuMemory.Read(messagePtr, reqDataSpan);
|
||||||
|
|
||||||
|
IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
|
||||||
|
|
||||||
|
ArrayPool<byte>.Shared.Return(reqData);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result)
|
||||||
|
{
|
||||||
|
FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result, int value)
|
||||||
|
{
|
||||||
|
Span<byte> span = stackalloc byte[sizeof(int)];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, value);
|
||||||
|
FillHipcResponse(response, result, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
response.Type = IpcMessageType.HipcResponse;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
_responseDataStream.SetLength(0);
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
writer.Write(IpcMagic.Sfco);
|
_responseDataStream.Write(IpcMagic.Sfco);
|
||||||
writer.Write(result);
|
_responseDataStream.Write(result);
|
||||||
|
|
||||||
if (data != null)
|
_responseDataStream.Write(data);
|
||||||
{
|
|
||||||
writer.Write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.RawData = ms.ToArray();
|
response.RawData = _responseDataStream.ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
@@ -372,6 +404,11 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
_sessions.Clear();
|
_sessions.Clear();
|
||||||
|
|
||||||
|
_requestDataReader.Dispose();
|
||||||
|
_requestDataStream.Dispose();
|
||||||
|
_responseDataWriter.Dispose();
|
||||||
|
_responseDataStream.Dispose();
|
||||||
|
|
||||||
InitDone.Dispose();
|
InitDone.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
public uint DataSize { get; }
|
public uint DataSize { get; }
|
||||||
public uint BssSize { get; }
|
public uint BssSize { get; }
|
||||||
|
|
||||||
public int[] Capabilities { get; }
|
public uint[] Capabilities { get; }
|
||||||
public bool UsesSecureMemory { get; }
|
public bool UsesSecureMemory { get; }
|
||||||
public bool Is64BitAddressSpace { get; }
|
public bool Is64BitAddressSpace { get; }
|
||||||
public bool Is64Bit { get; }
|
public bool Is64Bit { get; }
|
||||||
@@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
Version = reader.Version;
|
Version = reader.Version;
|
||||||
Name = reader.Name.ToString();
|
Name = reader.Name.ToString();
|
||||||
|
|
||||||
Capabilities = new int[32];
|
Capabilities = new uint[32];
|
||||||
|
|
||||||
for (int index = 0; index < Capabilities.Length; index++)
|
for (int index = 0; index < Capabilities.Length; index++)
|
||||||
{
|
{
|
||||||
Capabilities[index] = (int)reader.Capabilities[index];
|
Capabilities[index] = reader.Capabilities[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();
|
reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using Microsoft.IO;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -77,7 +79,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ulong position = context.Request.PtrBuff[index].Position;
|
ulong position = context.Request.PtrBuff[index].Position;
|
||||||
ulong size = context.Request.PtrBuff[index].Size;
|
ulong size = context.Request.PtrBuff[index].Size;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
while (size-- > 0)
|
while (size-- > 0)
|
||||||
{
|
{
|
||||||
@@ -91,7 +93,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(ms.ToArray());
|
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ulong position = context.Request.SendBuff[index].Position;
|
ulong position = context.Request.SendBuff[index].Position;
|
||||||
ulong size = context.Request.SendBuff[index].Size;
|
ulong size = context.Request.SendBuff[index].Size;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
while (size-- > 0)
|
while (size-- > 0)
|
||||||
{
|
{
|
||||||
@@ -124,7 +126,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
ms.WriteByte(value);
|
ms.WriteByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(ms.ToArray());
|
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.Input.Motion.CemuHook.Protocol;
|
using Ryujinx.Input.Motion.CemuHook.Protocol;
|
||||||
using System;
|
using System;
|
||||||
@@ -381,7 +382,7 @@ namespace Ryujinx.Input.Motion.CemuHook
|
|||||||
|
|
||||||
Header header = GenerateHeader(clientId);
|
Header header = GenerateHeader(clientId);
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(header);
|
writer.WriteStruct(header);
|
||||||
@@ -421,7 +422,7 @@ namespace Ryujinx.Input.Motion.CemuHook
|
|||||||
|
|
||||||
Header header = GenerateHeader(clientId);
|
Header header = GenerateHeader(clientId);
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
{
|
||||||
writer.WriteStruct(header);
|
writer.WriteStruct(header);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Memory
|
namespace Ryujinx.Memory
|
||||||
@@ -77,6 +78,21 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
||||||
void Write(ulong va, ReadOnlySpan<byte> data);
|
void Write(ulong va, ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data to CPU mapped memory, with write tracking.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">Virtual address to write the data into</param>
|
||||||
|
/// <param name="data">Data to be written</param>
|
||||||
|
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
|
||||||
|
public void Write(ulong va, ReadOnlySequence<byte> data)
|
||||||
|
{
|
||||||
|
foreach (ReadOnlyMemory<byte> segment in data)
|
||||||
|
{
|
||||||
|
Write(va, segment.Span);
|
||||||
|
va += (ulong)segment.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes data to the application process, returning false if the data was not changed.
|
/// Writes data to the application process, returning false if the data was not changed.
|
||||||
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
|
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
|
||||||
|
@@ -6,6 +6,7 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@@ -136,8 +137,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
|
||||||
using (MemoryStream streamPng = new MemoryStream())
|
using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
file.Get.AsStream().CopyTo(stream);
|
file.Get.AsStream().CopyTo(stream);
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
private byte[] ProcessImage(byte[] data)
|
private byte[] ProcessImage(byte[] data)
|
||||||
{
|
{
|
||||||
using (MemoryStream streamJpg = new MemoryStream())
|
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
Image avatarImage = Image.Load(data, new PngDecoder());
|
Image avatarImage = Image.Load(data, new PngDecoder());
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Gtk;
|
using Gtk;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
@@ -181,7 +182,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
{
|
{
|
||||||
image.Mutate(x => x.Resize(256, 256));
|
image.Mutate(x => x.Resize(256, 256));
|
||||||
|
|
||||||
using (MemoryStream streamJpg = new MemoryStream())
|
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
|
||||||
{
|
{
|
||||||
image.SaveAsJpeg(streamJpg);
|
image.SaveAsJpeg(streamJpg);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user